From 3da2f5e614c796372c76967afbb41a6ae5d5a41d Mon Sep 17 00:00:00 2001 From: Don Williams Date: Sat, 20 Sep 2025 13:44:10 -0400 Subject: fix(sddm): set Backgrounds/default as primary and update fallbacks; detect themes dir (incl. NixOS) and skip prompt on read-only paths MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Prefer Backgrounds/default for simple_sddm_2; update default.jpg/png if present - Detect SDDM themes dir under /usr/share and /run/current-system - Avoid prompting when theme is missing or Backgrounds is not writable (e.g., NixOS) - Skip runtime changes on NixOS since SDDM isn’t used in that config --- config/hypr/UserScripts/WallpaperEffects.sh | 60 +++++++++++++++++------------ config/hypr/UserScripts/WallpaperSelect.sh | 22 ++++++++--- config/hypr/scripts/sddm_wallpaper.sh | 23 ++++++++++- 3 files changed, 73 insertions(+), 32 deletions(-) (limited to 'config/hypr') diff --git a/config/hypr/UserScripts/WallpaperEffects.sh b/config/hypr/UserScripts/WallpaperEffects.sh index 2ba58d0c..ac8fc0e8 100755 --- a/config/hypr/UserScripts/WallpaperEffects.sh +++ b/config/hypr/UserScripts/WallpaperEffects.sh @@ -108,31 +108,41 @@ main sleep 1 if [[ -n "$choice" ]]; then - sddm_simple="/usr/share/sddm/themes/simple_sddm_2" - if [ -d "$sddm_simple" ]; then - - # Check if yad is running to avoid multiple yad notification - if pidof yad > /dev/null; then - killall yad - fi - - if yad --info --text="Set current wallpaper as SDDM background?\n\nNOTE: This only applies to SIMPLE SDDM v2 Theme" \ - --text-align=left \ - --title="SDDM Background" \ - --timeout=5 \ - --timeout-indicator=right \ - --button="yad-yes:0" \ - --button="yad-no:1" \ - ; then - - # Check if terminal exists - if ! command -v "$terminal" &>/dev/null; then - notify-send -i "$iDIR/ja.png" "Missing $terminal" "Install $terminal to enable setting of wallpaper background" - exit 1 - fi + # Resolve SDDM themes directory (standard and NixOS path) + sddm_themes_dir="" + if [ -d "/usr/share/sddm/themes" ]; then + sddm_themes_dir="/usr/share/sddm/themes" + elif [ -d "/run/current-system/sw/share/sddm/themes" ]; then + sddm_themes_dir="/run/current-system/sw/share/sddm/themes" + fi - exec $SCRIPTSDIR/sddm_wallpaper.sh --effects - + if [ -n "$sddm_themes_dir" ]; then + sddm_simple="$sddm_themes_dir/simple_sddm_2" + + # Only prompt if theme exists and its Backgrounds directory is writable + if [ -d "$sddm_simple" ] && [ -w "$sddm_simple/Backgrounds" ]; then + # Check if yad is running to avoid multiple yad notification + if pidof yad > /dev/null; then + killall yad + fi + + if yad --info --text="Set current wallpaper as SDDM background?\n\nNOTE: This only applies to SIMPLE SDDM v2 Theme" \ + --text-align=left \ + --title="SDDM Background" \ + --timeout=5 \ + --timeout-indicator=right \ + --button="yad-yes:0" \ + --button="yad-no:1" \ + ; then + + # Check if terminal exists + if ! command -v "$terminal" &>/dev/null; then + notify-send -i "$iDIR/ja.png" "Missing $terminal" "Install $terminal to enable setting of wallpaper background" + exit 1 + fi + + exec "$SCRIPTSDIR/sddm_wallpaper.sh" --effects + fi fi fi -fi \ No newline at end of file +fi diff --git a/config/hypr/UserScripts/WallpaperSelect.sh b/config/hypr/UserScripts/WallpaperSelect.sh index a08b53ce..466832ba 100755 --- a/config/hypr/UserScripts/WallpaperSelect.sh +++ b/config/hypr/UserScripts/WallpaperSelect.sh @@ -101,9 +101,21 @@ menu() { # Offer SDDM Simple Wallpaper Option (only for non-video wallpapers) set_sddm_wallpaper() { sleep 1 - sddm_simple="/usr/share/sddm/themes/simple_sddm_2" - if [ -d "$sddm_simple" ]; then + # Resolve SDDM themes directory (standard and NixOS path) + local sddm_themes_dir="" + if [ -d "/usr/share/sddm/themes" ]; then + sddm_themes_dir="/usr/share/sddm/themes" + elif [ -d "/run/current-system/sw/share/sddm/themes" ]; then + sddm_themes_dir="/run/current-system/sw/share/sddm/themes" + fi + + [ -z "$sddm_themes_dir" ] && return 0 + + local sddm_simple="$sddm_themes_dir/simple_sddm_2" + + # Only prompt if theme exists and its Backgrounds directory is writable + if [ -d "$sddm_simple" ] && [ -w "$sddm_simple/Backgrounds" ]; then # Check if yad is running to avoid multiple notifications if pidof yad >/dev/null; then @@ -123,9 +135,9 @@ set_sddm_wallpaper() { notify-send -i "$iDIR/error.png" "Missing $terminal" "Install $terminal to enable setting of wallpaper background" exit 1 fi - - exec $SCRIPTSDIR/sddm_wallpaper.sh --normal - + + exec "$SCRIPTSDIR/sddm_wallpaper.sh" --normal + fi fi } diff --git a/config/hypr/scripts/sddm_wallpaper.sh b/config/hypr/scripts/sddm_wallpaper.sh index a781156e..9487188c 100644 --- a/config/hypr/scripts/sddm_wallpaper.sh +++ b/config/hypr/scripts/sddm_wallpaper.sh @@ -10,7 +10,12 @@ wallDIR="$HOME/Pictures/wallpapers" SCRIPTSDIR="$HOME/.config/hypr/scripts" wallpaper_current="$HOME/.config/hypr/wallpaper_effects/.wallpaper_current" wallpaper_modified="$HOME/.config/hypr/wallpaper_effects/.wallpaper_modified" -sddm_simple="/usr/share/sddm/themes/simple_sddm_2" +# Resolve SDDM themes directory (standard paths and NixOS path) +sddm_themes_dir="/usr/share/sddm/themes" +if [ ! -d "$sddm_themes_dir" ] && [ -d "/run/current-system/sw/share/sddm/themes" ]; then + sddm_themes_dir="/run/current-system/sw/share/sddm/themes" +fi +sddm_simple="$sddm_themes_dir/simple_sddm_2" # rofi-wallust-sddm colors path rofi_wallust="$HOME/.config/rofi/wallust/colors-rofi.rasi" @@ -46,6 +51,12 @@ else wallpaper_path="$wallpaper_modified" fi +# Abort on NixOS where this repo doesn't manage SDDM and themes are typically read-only +if hostnamectl 2>/dev/null | grep -q 'Operating System: NixOS'; then + notify-send -i "$iDIR/error.png" "SDDM" "NixOS detected: skipping SDDM background change." + exit 0 +fi + # Launch terminal and apply changes $terminal -e bash -c " echo 'Enter your password to update SDDM wallpapers and colors'; @@ -70,7 +81,15 @@ sudo sed -i \"s/UserIconColor=\\\"#.*\\\"/UserIconColor=\\\"$color7\\\"/\" \"$sd sudo sed -i \"s/PasswordIconColor=\\\"#.*\\\"/PasswordIconColor=\\\"$color7\\\"/\" \"$sddm_theme_conf\" # Copy wallpaper to SDDM theme -sudo cp \"$wallpaper_path\" \"$sddm_simple/Backgrounds/default\" +# Primary: set Backgrounds/default (no extension) for simple_sddm_2 +sudo cp -f \"$wallpaper_path\" \"$sddm_simple/Backgrounds/default\" || true +# Fallbacks: if theme ships default.jpg or default.png, update those too +if [ -e \"$sddm_simple/Backgrounds/default.jpg\" ]; then + sudo cp -f \"$wallpaper_path\" \"$sddm_simple/Backgrounds/default.jpg\" +fi +if [ -e \"$sddm_simple/Backgrounds/default.png\" ]; then + sudo cp -f \"$wallpaper_path\" \"$sddm_simple/Backgrounds/default.png\" +fi notify-send -i \"$iDIR/ja.png\" \"SDDM\" \"Background SET\" " \ No newline at end of file -- cgit v1.2.3 From a38599d1a0d70ed182b671bed47fefba1168dead Mon Sep 17 00:00:00 2001 From: Donald Williams <129223418+dwilliam62@users.noreply.github.com> Date: Tue, 30 Sep 2025 13:02:56 -0400 Subject: Update Startup_Apps.conf improve dropdown See bug 810 https://github.com/JaKooLit/Hyprland-Dots/issues/810#issuecomment-3351947644 85% of the time when trying to use the keybindings for the drop down terminal, including custom keybindings, it hides the application in the background, or brings an app in a different workspace into the current one, instead of the terminal popping up. I tested this and it works well for me --- config/hypr/UserConfigs/Startup_Apps.conf | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'config/hypr') diff --git a/config/hypr/UserConfigs/Startup_Apps.conf b/config/hypr/UserConfigs/Startup_Apps.conf index f8af55e4..7b668b77 100644 --- a/config/hypr/UserConfigs/Startup_Apps.conf +++ b/config/hypr/UserConfigs/Startup_Apps.conf @@ -20,6 +20,10 @@ exec-once = swww-daemon --format xrgb exec-once = dbus-update-activation-environment --systemd WAYLAND_DISPLAY XDG_CURRENT_DESKTOP exec-once = systemctl --user import-environment WAYLAND_DISPLAY XDG_CURRENT_DESKTOP +# Initialize Drop Down terminal - See Bug#810 https://github.com/JaKooLit/Hyprland-Dots/issues/810#issuecomment-3351947644 +exec-once = $HOME/.config/hypr/scripts/Dropterminal.sh kitty & + + # Polkit (Polkit Gnome / KDE) exec-once = $scriptsDir/Polkit.sh -- cgit v1.2.3 From b6403a91781ab071f9efce4657cee81472afb66b Mon Sep 17 00:00:00 2001 From: mio-dokuhaki Date: Wed, 1 Oct 2025 23:05:13 +0900 Subject: Bug fix for sending incorrect playing status --- config/hypr/scripts/MediaCtrl.sh | 65 ++++++++++++++++++++-------------------- 1 file changed, 33 insertions(+), 32 deletions(-) (limited to 'config/hypr') diff --git a/config/hypr/scripts/MediaCtrl.sh b/config/hypr/scripts/MediaCtrl.sh index 2cbeccf3..000c3ade 100755 --- a/config/hypr/scripts/MediaCtrl.sh +++ b/config/hypr/scripts/MediaCtrl.sh @@ -6,56 +6,57 @@ music_icon="$HOME/.config/swaync/icons/music.png" # Play the next track play_next() { - playerctl next - show_music_notification + playerctl next + show_music_notification } # Play the previous track play_previous() { - playerctl previous - show_music_notification + playerctl previous + show_music_notification } # Toggle play/pause toggle_play_pause() { - playerctl play-pause - show_music_notification + playerctl play-pause + sleep 0.1 + show_music_notification } # Stop playback stop_playback() { - playerctl stop - notify-send -e -u low -i $music_icon " Playback:" " Stopped" + playerctl stop + notify-send -e -u low -i $music_icon " Playback:" " Stopped" } # Display notification with song information show_music_notification() { - status=$(playerctl status) - if [[ "$status" == "Playing" ]]; then - song_title=$(playerctl metadata title) - song_artist=$(playerctl metadata artist) - notify-send -e -u low -i $music_icon "Now Playing:" "$song_title by $song_artist" - elif [[ "$status" == "Paused" ]]; then - notify-send -e -u low -i $music_icon " Playback:" " Paused" - fi + status=$(playerctl status) + if [[ "$status" == "Playing" ]]; then + song_title=$(playerctl metadata title) + song_artist=$(playerctl metadata artist) + notify-send -e -u low -i $music_icon "Now Playing:" "$song_title by $song_artist" + elif [[ "$status" == "Paused" ]]; then + notify-send -e -u low -i $music_icon " Playback:" " Paused" + fi } # Get media control action from command line argument case "$1" in - "--nxt") - play_next - ;; - "--prv") - play_previous - ;; - "--pause") - toggle_play_pause - ;; - "--stop") - stop_playback - ;; - *) - echo "Usage: $0 [--nxt|--prv|--pause|--stop]" - exit 1 - ;; +"--nxt") + play_next + ;; +"--prv") + play_previous + ;; +"--pause") + toggle_play_pause + ;; +"--stop") + stop_playback + ;; +*) + echo "Usage: $0 [--nxt|--prv|--pause|--stop]" + exit 1 + ;; esac -- cgit v1.2.3 From 613b232adb2f26b0bc89464a5003271a8559e6f0 Mon Sep 17 00:00:00 2001 From: Donald Williams <129223418+dwilliam62@users.noreply.github.com> Date: Sat, 4 Oct 2025 17:21:49 -0400 Subject: Update WindowRules.conf Fixing windowrule for opacity --- config/hypr/UserConfigs/WindowRules.conf | 1 + 1 file changed, 1 insertion(+) (limited to 'config/hypr') diff --git a/config/hypr/UserConfigs/WindowRules.conf b/config/hypr/UserConfigs/WindowRules.conf index e5fd11b9..a5a6e4a3 100644 --- a/config/hypr/UserConfigs/WindowRules.conf +++ b/config/hypr/UserConfigs/WindowRules.conf @@ -180,6 +180,7 @@ windowrule = opacity 0.8 0.7, class:^(gedit|org.gnome.TextEditor|mousepad)$ windowrule = opacity 0.9 0.8, class:^(deluge)$ windowrule = opacity 0.9 0.8, class:^(seahorse)$ # gnome-keyring gui windowrule = opacity 0.95 0.75, title:^(Picture-in-Picture)$ +windowrule = opacity 0.9,class:^(code)$ # SIZE windowrule = size 65% 90%, tag:KooL_Cheat* -- cgit v1.2.3 From 5c41160260900398b8f9440edf0c735946187e90 Mon Sep 17 00:00:00 2001 From: Don Williams Date: Sun, 5 Oct 2025 14:36:19 -0400 Subject: feat(hyprsunset): add Hyprsunset toggle script, SUPER+N keybind, and Waybar nightlight button across all layouts; include in status and vertical power groups --- .gitignore | 3 + config/hypr/UserConfigs/UserKeybinds.conf | 4 ++ config/hypr/scripts/Hyprsunset.sh | 77 ++++++++++++++++++++++++ config/waybar/ModulesCustom | 11 ++++ config/waybar/ModulesGroups | 2 + config/waybar/configs/[BOT & Left] SouthWest | 1 + config/waybar/configs/[BOT & Right] SouthEast | 1 + config/waybar/configs/[LEFT] WestWing | 1 + config/waybar/configs/[LEFT] WestWing v2 | 1 + config/waybar/configs/[RIGHT] EastWing | 1 + config/waybar/configs/[RIGHT] EastWing v2 | 1 + config/waybar/configs/[TOP & BOT] SummitSplit | 2 + config/waybar/configs/[TOP & BOT] SummitSplit v2 | 3 +- config/waybar/configs/[TOP] 0-Ja-0 | 1 + config/waybar/configs/[TOP] Arrow | 3 +- config/waybar/configs/[TOP] Camellia | 3 +- config/waybar/configs/[TOP] Chrysanthemum | 3 +- config/waybar/configs/[TOP] Everforest | 1 + config/waybar/configs/[TOP] Gardenia | 3 +- config/waybar/configs/[TOP] Minimal - Long | 1 + config/waybar/configs/[TOP] Minimal - Short | 1 + config/waybar/configs/[TOP] Peony | 3 +- config/waybar/configs/[TOP] Sleek | 1 + 23 files changed, 122 insertions(+), 6 deletions(-) create mode 100644 .gitignore create mode 100755 config/hypr/scripts/Hyprsunset.sh (limited to 'config/hypr') diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..d20a9670 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +WARP.md +.warp.md +result diff --git a/config/hypr/UserConfigs/UserKeybinds.conf b/config/hypr/UserConfigs/UserKeybinds.conf index 2f0e808f..d8a1d52e 100644 --- a/config/hypr/UserConfigs/UserKeybinds.conf +++ b/config/hypr/UserConfigs/UserKeybinds.conf @@ -29,6 +29,7 @@ bind = $mainMod, H, exec, $scriptsDir/KeyHints.sh # help / cheat sheet bind = $mainMod ALT, R, exec, $scriptsDir/Refresh.sh # Refresh waybar, swaync, rofi bind = $mainMod ALT, E, exec, $scriptsDir/RofiEmoji.sh # emoji menu bind = $mainMod, S, exec, $scriptsDir/RofiSearch.sh # Google search using rofi +bind = $mainMod SHIFT, S, exec, rofi -show windows # list/switch apps using rofi bind = $mainMod ALT, O, exec, $scriptsDir/ChangeBlur.sh # Toggle blur settings bind = $mainMod SHIFT, G, exec, $scriptsDir/GameMode.sh # Toggle animations ON/OFF bind = $mainMod ALT, L, exec, $scriptsDir/ChangeLayout.sh # Toggle Master or Dwindle Layout @@ -55,6 +56,9 @@ bind = $mainMod CTRL ALT, B, exec, pkill -SIGUSR1 waybar # Toggle hide/show wayb bind = $mainMod CTRL, B, exec, $scriptsDir/WaybarStyles.sh # Waybar Styles Menu bind = $mainMod ALT, B, exec, $scriptsDir/WaybarLayout.sh # Waybar Layout Menu +# Night light toggle (Hyprsunset) +bind = $mainMod, N, exec, $scriptsDir/Hyprsunset.sh toggle + # FEATURES / EXTRAS (UserScripts) bind = $mainMod SHIFT, M, exec, $UserScripts/RofiBeats.sh # online music using rofi bind = $mainMod, W, exec, $UserScripts/WallpaperSelect.sh # Select wallpaper to apply diff --git a/config/hypr/scripts/Hyprsunset.sh b/config/hypr/scripts/Hyprsunset.sh new file mode 100755 index 00000000..68e5fab8 --- /dev/null +++ b/config/hypr/scripts/Hyprsunset.sh @@ -0,0 +1,77 @@ +#!/usr/bin/env bash +set -euo pipefail + +# Hyprsunset toggle + Waybar status helper +# Phase 1: manual toggle only (no scheduling) +# Icons: +# - Off: bright sun +# - On: sunset icon if available, otherwise a blue sun +# +# Customize via env vars: +# HYPERSUNSET_TEMP default 4500 (K) +# HYPERSUNSET_ICON_MODE sunset|blue (default: sunset) + +STATE_FILE="$HOME/.cache/.hyprsunset_state" +TARGET_TEMP="${HYPERSUNSET_TEMP:-4500}" +ICON_MODE="${HYPERSUNSET_ICON_MODE:-sunset}" + +ensure_state() { + [[ -f "$STATE_FILE" ]] || echo "off" > "$STATE_FILE" +} + +# Render icons using pango markup to allow colorization +icon_off() { + # bright sun when not activated + printf "" +} + +icon_on() { + case "$ICON_MODE" in + sunset) + printf "" + ;; + blue) + printf "" + ;; + *) + printf "" + ;; + esac +} + +cmd_toggle() { + ensure_state + state="$(cat "$STATE_FILE" || echo off)" + if [[ "$state" == "on" ]]; then + if command -v hyprsunset >/dev/null 2>&1; then + hyprsunset -r || true + fi + echo off > "$STATE_FILE" + else + if command -v hyprsunset >/dev/null 2>&1; then + hyprsunset -t "$TARGET_TEMP" || true + fi + echo on > "$STATE_FILE" + fi +} + +cmd_status() { + ensure_state + state="$(cat "$STATE_FILE" || echo off)" + if [[ "$state" == "on" ]]; then + txt="$(icon_on)" + cls="on" + tip="Night light on @ ${TARGET_TEMP}K" + else + txt="$(icon_off)" + cls="off" + tip="Night light off" + fi + printf '{"text":"%s","class":"%s","tooltip":"%s"}\n' "$txt" "$cls" "$tip" +} + +case "${1:-}" in + toggle) cmd_toggle ;; + status) cmd_status ;; + *) echo "usage: $0 [toggle|status]" >&2; exit 2 ;; + esac diff --git a/config/waybar/ModulesCustom b/config/waybar/ModulesCustom index dddc5ccc..1f4afa20 100644 --- a/config/waybar/ModulesCustom +++ b/config/waybar/ModulesCustom @@ -104,6 +104,17 @@ "tooltip-format": "Left Click: Switch Dark-Light Themes\nMiddle Click: Wallpaper Menu\nRight Click: Waybar Styles Menu", }, +// Night light (Hyprsunset) +"custom/nightlight": { + "return-type": "json", + "exec": "$HOME/.config/hypr/scripts/Hyprsunset.sh status", + "interval": 5, + "format": "{}", + "on-click": "$HOME/.config/hypr/scripts/Hyprsunset.sh toggle", + "tooltip": true, + "tooltip-format": "Night light toggle" +}, + "custom/lock": { "format": "󰌾", "on-click": "$HOME/.config/hypr/scripts/LockScreen.sh", diff --git a/config/waybar/ModulesGroups b/config/waybar/ModulesGroups index 30e47f16..8d4453a2 100644 --- a/config/waybar/ModulesGroups +++ b/config/waybar/ModulesGroups @@ -89,6 +89,7 @@ }, "modules": [ "custom/power", + "custom/nightlight", "custom/lock", "keyboard-state", "custom/keyboard", @@ -131,6 +132,7 @@ }, "modules": [ "custom/power", + "custom/nightlight", "custom/lock", "custom/logout", "custom/reboot" diff --git a/config/waybar/configs/[BOT & Left] SouthWest b/config/waybar/configs/[BOT & Left] SouthWest index a039f040..594b46d3 100644 --- a/config/waybar/configs/[BOT & Left] SouthWest +++ b/config/waybar/configs/[BOT & Left] SouthWest @@ -46,6 +46,7 @@ "power-profiles-daemon", "pulseaudio#microphone", "keyboard-state", + "custom/nightlight", "custom/power", ], }, diff --git a/config/waybar/configs/[BOT & Right] SouthEast b/config/waybar/configs/[BOT & Right] SouthEast index 9a58e952..03cdb06c 100644 --- a/config/waybar/configs/[BOT & Right] SouthEast +++ b/config/waybar/configs/[BOT & Right] SouthEast @@ -46,6 +46,7 @@ "power-profiles-daemon", "pulseaudio#microphone", "keyboard-state", + "custom/nightlight", "custom/power", ], }, diff --git a/config/waybar/configs/[LEFT] WestWing b/config/waybar/configs/[LEFT] WestWing index e3f25d1a..28e5dbec 100644 --- a/config/waybar/configs/[LEFT] WestWing +++ b/config/waybar/configs/[LEFT] WestWing @@ -39,6 +39,7 @@ "backlight#vertical", "pulseaudio#microphone_vertical", "pulseaudio#vertical", + "custom/nightlight", "custom/power_vertical", "custom/menu", ], diff --git a/config/waybar/configs/[LEFT] WestWing v2 b/config/waybar/configs/[LEFT] WestWing v2 index f1ed69c3..906f83d6 100644 --- a/config/waybar/configs/[LEFT] WestWing v2 +++ b/config/waybar/configs/[LEFT] WestWing v2 @@ -40,6 +40,7 @@ "backlight#vertical", "pulseaudio/slider", "pulseaudio#microphone_vertical", + "custom/nightlight", "group/power#vert", ], diff --git a/config/waybar/configs/[RIGHT] EastWing b/config/waybar/configs/[RIGHT] EastWing index b64fe5f8..a5ce6756 100644 --- a/config/waybar/configs/[RIGHT] EastWing +++ b/config/waybar/configs/[RIGHT] EastWing @@ -39,6 +39,7 @@ "backlight#vertical", "pulseaudio#microphone_vertical", "pulseaudio#vertical", + "custom/nightlight", "custom/power_vertical", "custom/menu", ], diff --git a/config/waybar/configs/[RIGHT] EastWing v2 b/config/waybar/configs/[RIGHT] EastWing v2 index f9991bd6..28dd1e43 100644 --- a/config/waybar/configs/[RIGHT] EastWing v2 +++ b/config/waybar/configs/[RIGHT] EastWing v2 @@ -40,6 +40,7 @@ "backlight#vertical", "pulseaudio/slider", "pulseaudio#microphone_vertical", + "custom/nightlight", "group/power#vert", ], diff --git a/config/waybar/configs/[TOP & BOT] SummitSplit b/config/waybar/configs/[TOP & BOT] SummitSplit index 03c8e81b..516e9834 100644 --- a/config/waybar/configs/[TOP & BOT] SummitSplit +++ b/config/waybar/configs/[TOP & BOT] SummitSplit @@ -44,6 +44,7 @@ "network", "custom/updater", "custom/cycle_wall", + "custom/nightlight", "custom/lock", ], }, @@ -88,6 +89,7 @@ "pulseaudio", //"wireplumber", "pulseaudio#microphone", + "custom/nightlight", "custom/power", ], }], diff --git a/config/waybar/configs/[TOP & BOT] SummitSplit v2 b/config/waybar/configs/[TOP & BOT] SummitSplit v2 index 1425f657..4d576aef 100644 --- a/config/waybar/configs/[TOP & BOT] SummitSplit v2 +++ b/config/waybar/configs/[TOP & BOT] SummitSplit v2 @@ -28,13 +28,14 @@ "network", ], "modules-center": ["hyprland/window"], - "modules-right": [ +"modules-right": [ "mpris", "battery", "backlight", "pulseaudio", "group/mobo_drawer", "idle_inhibitor", + "custom/nightlight", "group/power" ], diff --git a/config/waybar/configs/[TOP] 0-Ja-0 b/config/waybar/configs/[TOP] 0-Ja-0 index c4cb0a65..6e7fc9aa 100644 --- a/config/waybar/configs/[TOP] 0-Ja-0 +++ b/config/waybar/configs/[TOP] 0-Ja-0 @@ -48,6 +48,7 @@ "custom/separator#dot-line", "mpris", "custom/separator#blank", + "custom/nightlight", "group/status", ], } \ No newline at end of file diff --git a/config/waybar/configs/[TOP] Arrow b/config/waybar/configs/[TOP] Arrow index 8001d8fb..7fc55f60 100644 --- a/config/waybar/configs/[TOP] Arrow +++ b/config/waybar/configs/[TOP] Arrow @@ -36,7 +36,8 @@ "battery", "custom/arrow2", "tray", - "custom/arrow1", +"custom/arrow1", + "custom/nightlight", "clock#2" ], } diff --git a/config/waybar/configs/[TOP] Camellia b/config/waybar/configs/[TOP] Camellia index c93e9079..efaf6e20 100644 --- a/config/waybar/configs/[TOP] Camellia +++ b/config/waybar/configs/[TOP] Camellia @@ -44,7 +44,8 @@ "power-profiles-daemon", "battery", "clock#3", - "network"], + "network", + "custom/nightlight"], // Additional modules // "pulseaudio/slider": { diff --git a/config/waybar/configs/[TOP] Chrysanthemum b/config/waybar/configs/[TOP] Chrysanthemum index ebaa0ca4..d12f73e3 100644 --- a/config/waybar/configs/[TOP] Chrysanthemum +++ b/config/waybar/configs/[TOP] Chrysanthemum @@ -36,6 +36,7 @@ "modules-right": [ "pulseaudio#1", "backlight#2", - "battery"], + "battery", + "custom/nightlight"], } \ No newline at end of file diff --git a/config/waybar/configs/[TOP] Everforest b/config/waybar/configs/[TOP] Everforest index a66763ed..7b007f78 100644 --- a/config/waybar/configs/[TOP] Everforest +++ b/config/waybar/configs/[TOP] Everforest @@ -45,6 +45,7 @@ "battery#forest", "custom/separator#blank_2", "group/audio", + "custom/nightlight", ], // Additional / Edited Waybar Modules // diff --git a/config/waybar/configs/[TOP] Gardenia b/config/waybar/configs/[TOP] Gardenia index 77e86bae..073ff46e 100644 --- a/config/waybar/configs/[TOP] Gardenia +++ b/config/waybar/configs/[TOP] Gardenia @@ -37,7 +37,8 @@ "modules-right": [ "pulseaudio#1", "backlight#2", - "battery" + "battery", + "custom/nightlight" ], } \ No newline at end of file diff --git a/config/waybar/configs/[TOP] Minimal - Long b/config/waybar/configs/[TOP] Minimal - Long index b57cf3a3..a5be4bd7 100644 --- a/config/waybar/configs/[TOP] Minimal - Long +++ b/config/waybar/configs/[TOP] Minimal - Long @@ -42,6 +42,7 @@ "custom/separator#blank_2", "group/audio", "custom/separator#blank_2", + "custom/nightlight", "custom/power", ], diff --git a/config/waybar/configs/[TOP] Minimal - Short b/config/waybar/configs/[TOP] Minimal - Short index 57abd3d3..7b9a1929 100644 --- a/config/waybar/configs/[TOP] Minimal - Short +++ b/config/waybar/configs/[TOP] Minimal - Short @@ -34,5 +34,6 @@ "backlight", "pulseaudio", "battery", + "custom/nightlight", "custom/power"], } diff --git a/config/waybar/configs/[TOP] Peony b/config/waybar/configs/[TOP] Peony index 2fd1dfe3..a1ef02e8 100644 --- a/config/waybar/configs/[TOP] Peony +++ b/config/waybar/configs/[TOP] Peony @@ -44,6 +44,7 @@ "temperature", "custom/separator#blank", "group/mobo_drawer", - "network"], + "network", + "custom/nightlight"], } \ No newline at end of file diff --git a/config/waybar/configs/[TOP] Sleek b/config/waybar/configs/[TOP] Sleek index fe0f41ba..f591f472 100644 --- a/config/waybar/configs/[TOP] Sleek +++ b/config/waybar/configs/[TOP] Sleek @@ -39,6 +39,7 @@ "pulseaudio", "custom/separator#blank", "group/mobo_drawer", + "custom/nightlight", "custom/power", ], } -- cgit v1.2.3 From 4b2fb9ebeb2abaeb52f24ed4889801b36cbb2ba9 Mon Sep 17 00:00:00 2001 From: Don Williams Date: Sun, 5 Oct 2025 14:53:46 -0400 Subject: fix(hyprsunset): support hyprsunset v0.3.x (-i instead of -r), avoid CTM conflicts, run in background, process-based status --- config/hypr/scripts/Hyprsunset.sh | 26 ++++++++++++++++++++++---- 1 file changed, 22 insertions(+), 4 deletions(-) (limited to 'config/hypr') diff --git a/config/hypr/scripts/Hyprsunset.sh b/config/hypr/scripts/Hyprsunset.sh index 68e5fab8..0de19d44 100755 --- a/config/hypr/scripts/Hyprsunset.sh +++ b/config/hypr/scripts/Hyprsunset.sh @@ -42,14 +42,26 @@ icon_on() { cmd_toggle() { ensure_state state="$(cat "$STATE_FILE" || echo off)" + + # Always stop any running hyprsunset first to avoid CTM manager conflicts + if pgrep -x hyprsunset >/dev/null 2>&1; then + pkill -x hyprsunset || true + # give it a moment to release the CTM manager + sleep 0.2 + fi + if [[ "$state" == "on" ]]; then + # Turning OFF: set identity and exit if command -v hyprsunset >/dev/null 2>&1; then - hyprsunset -r || true + nohup hyprsunset -i >/dev/null 2>&1 & + # if hyprsunset persists, stop it shortly after applying identity + sleep 0.3 && pkill -x hyprsunset || true fi echo off > "$STATE_FILE" else + # Turning ON: start hyprsunset at target temp in background if command -v hyprsunset >/dev/null 2>&1; then - hyprsunset -t "$TARGET_TEMP" || true + nohup hyprsunset -t "$TARGET_TEMP" >/dev/null 2>&1 & fi echo on > "$STATE_FILE" fi @@ -57,8 +69,14 @@ cmd_toggle() { cmd_status() { ensure_state - state="$(cat "$STATE_FILE" || echo off)" - if [[ "$state" == "on" ]]; then + # Prefer live process detection; fall back to state file + if pgrep -x hyprsunset >/dev/null 2>&1; then + onoff="on" + else + onoff="$(cat "$STATE_FILE" || echo off)" + fi + + if [[ "$onoff" == "on" ]]; then txt="$(icon_on)" cls="on" tip="Night light on @ ${TARGET_TEMP}K" -- cgit v1.2.3 From d200f9134d5e6bbc558b8acaea64229ba7b420cb Mon Sep 17 00:00:00 2001 From: Don Williams Date: Sun, 5 Oct 2025 15:08:38 -0400 Subject: feat(hyprsunset): add libnotify messages on toggle; use plain glyph text; ensure nightlight button is visible in [TOP] Default Laptop; set escape=false for custom/nightlight --- config/hypr/scripts/Hyprsunset.sh | 15 +++++++++------ config/waybar/ModulesCustom | 3 ++- config/waybar/configs/[TOP] Default Laptop | 1 + 3 files changed, 12 insertions(+), 7 deletions(-) (limited to 'config/hypr') diff --git a/config/hypr/scripts/Hyprsunset.sh b/config/hypr/scripts/Hyprsunset.sh index 0de19d44..0372d720 100755 --- a/config/hypr/scripts/Hyprsunset.sh +++ b/config/hypr/scripts/Hyprsunset.sh @@ -21,20 +21,21 @@ ensure_state() { # Render icons using pango markup to allow colorization icon_off() { - # bright sun when not activated - printf "" + # bright sun when not activated (plain glyph; styling via Waybar CSS by class) + printf "" } icon_on() { case "$ICON_MODE" in sunset) - printf "" + # fallback to same glyph; color can be handled by CSS if desired + printf "" ;; blue) - printf "" + printf "" ;; *) - printf "" + printf "" ;; esac } @@ -50,7 +51,7 @@ cmd_toggle() { sleep 0.2 fi - if [[ "$state" == "on" ]]; then +if [[ "$state" == "on" ]]; then # Turning OFF: set identity and exit if command -v hyprsunset >/dev/null 2>&1; then nohup hyprsunset -i >/dev/null 2>&1 & @@ -58,12 +59,14 @@ cmd_toggle() { sleep 0.3 && pkill -x hyprsunset || true fi echo off > "$STATE_FILE" + notify-send -u low "Hyprsunset: Disabled" || true else # Turning ON: start hyprsunset at target temp in background if command -v hyprsunset >/dev/null 2>&1; then nohup hyprsunset -t "$TARGET_TEMP" >/dev/null 2>&1 & fi echo on > "$STATE_FILE" + notify-send -u low "Hyprsunset: Enabled" "${TARGET_TEMP}K" || true fi } diff --git a/config/waybar/ModulesCustom b/config/waybar/ModulesCustom index 1f4afa20..512ff751 100644 --- a/config/waybar/ModulesCustom +++ b/config/waybar/ModulesCustom @@ -112,7 +112,8 @@ "format": "{}", "on-click": "$HOME/.config/hypr/scripts/Hyprsunset.sh toggle", "tooltip": true, - "tooltip-format": "Night light toggle" + "tooltip-format": "Night light toggle", + "escape": false }, "custom/lock": { diff --git a/config/waybar/configs/[TOP] Default Laptop b/config/waybar/configs/[TOP] Default Laptop index b9722b89..0b264c6b 100644 --- a/config/waybar/configs/[TOP] Default Laptop +++ b/config/waybar/configs/[TOP] Default Laptop @@ -54,6 +54,7 @@ "custom/separator#line", "group/audio", "custom/separator#dot-line", + "custom/nightlight", "group/status", ], } \ No newline at end of file -- cgit v1.2.3 From eeb7d323e179e899bb21ae494b0052f0196362f9 Mon Sep 17 00:00:00 2001 From: Don Williams Date: Sun, 5 Oct 2025 15:23:05 -0400 Subject: fix(waybar/nightlight): use universal icons (☀/🌇) and explicit {text} format for JSON MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- config/hypr/scripts/Hyprsunset.sh | 13 +++++++------ config/waybar/ModulesCustom | 2 +- 2 files changed, 8 insertions(+), 7 deletions(-) (limited to 'config/hypr') diff --git a/config/hypr/scripts/Hyprsunset.sh b/config/hypr/scripts/Hyprsunset.sh index 0372d720..1e84bc66 100755 --- a/config/hypr/scripts/Hyprsunset.sh +++ b/config/hypr/scripts/Hyprsunset.sh @@ -21,21 +21,22 @@ ensure_state() { # Render icons using pango markup to allow colorization icon_off() { - # bright sun when not activated (plain glyph; styling via Waybar CSS by class) - printf "" + # universally available sun symbol + printf "☀" } icon_on() { case "$ICON_MODE" in sunset) - # fallback to same glyph; color can be handled by CSS if desired - printf "" + # sunset emoji (falls back to tofu if no emoji font) + printf "🌇" ;; blue) - printf "" + # no color in text; rely on CSS .on to style if desired + printf "☀" ;; *) - printf "" + printf "☀" ;; esac } diff --git a/config/waybar/ModulesCustom b/config/waybar/ModulesCustom index 512ff751..f4e95433 100644 --- a/config/waybar/ModulesCustom +++ b/config/waybar/ModulesCustom @@ -109,7 +109,7 @@ "return-type": "json", "exec": "$HOME/.config/hypr/scripts/Hyprsunset.sh status", "interval": 5, - "format": "{}", + "format": "{text}", "on-click": "$HOME/.config/hypr/scripts/Hyprsunset.sh toggle", "tooltip": true, "tooltip-format": "Night light toggle", -- cgit v1.2.3 From f24d5aed8e4362f42a022a52c16accbcb5b69c66 Mon Sep 17 00:00:00 2001 From: Don Williams Date: Sun, 5 Oct 2025 17:02:52 -0400 Subject: feat(nightlight/ui): enlarge icon via Pango markup (escape=false), reserve space (min-length) --- config/hypr/scripts/Hyprsunset.sh | 4 ++-- config/waybar/ModulesCustom | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) (limited to 'config/hypr') diff --git a/config/hypr/scripts/Hyprsunset.sh b/config/hypr/scripts/Hyprsunset.sh index 1e84bc66..c7c4b395 100755 --- a/config/hypr/scripts/Hyprsunset.sh +++ b/config/hypr/scripts/Hyprsunset.sh @@ -81,11 +81,11 @@ cmd_status() { fi if [[ "$onoff" == "on" ]]; then - txt="$(icon_on)" + txt="$(icon_on)" cls="on" tip="Night light on @ ${TARGET_TEMP}K" else - txt="$(icon_off)" + txt="$(icon_off)" cls="off" tip="Night light off" fi diff --git a/config/waybar/ModulesCustom b/config/waybar/ModulesCustom index f4e95433..146ec275 100644 --- a/config/waybar/ModulesCustom +++ b/config/waybar/ModulesCustom @@ -108,8 +108,9 @@ "custom/nightlight": { "return-type": "json", "exec": "$HOME/.config/hypr/scripts/Hyprsunset.sh status", - "interval": 5, + "interval": 3, "format": "{text}", + "min-length": 2, "on-click": "$HOME/.config/hypr/scripts/Hyprsunset.sh toggle", "tooltip": true, "tooltip-format": "Night light toggle", -- cgit v1.2.3 From 2ddd87fd710a75ca51c291c5f4b7fdd40b93d533 Mon Sep 17 00:00:00 2001 From: Don Williams Date: Sun, 5 Oct 2025 20:34:06 -0400 Subject: Updated weather.py should be same as development branch On branch ddubs-hyprsunset Your branch is up to date with 'origin/ddubs-hyprsunset'. Changes to be committed: modified: config/hypr/UserScripts/Weather.py --- config/hypr/UserScripts/Weather.py | 627 ++++++++++++++++++++++++++++++------- 1 file changed, 512 insertions(+), 115 deletions(-) (limited to 'config/hypr') diff --git a/config/hypr/UserScripts/Weather.py b/config/hypr/UserScripts/Weather.py index a8b70417..ca1d5281 100755 --- a/config/hypr/UserScripts/Weather.py +++ b/config/hypr/UserScripts/Weather.py @@ -1,15 +1,69 @@ #!/usr/bin/env python3 # /* ---- 💫 https://github.com/JaKooLit 💫 ---- */ # -# original code https://gist.github.com/Surendrajat/ff3876fd2166dd86fb71180f4e9342d7 -# weather using python +# Rewritten to use Open-Meteo APIs (worldwide, no API key) for robust weather data. +# Outputs Waybar-compatible JSON and a simple text cache. -import requests import json import os -from pyquery import PyQuery # install using `pip install pyquery` +import sys +import time +import html +from typing import Any, Dict, List, Optional, Tuple + +import requests + +# =============== Configuration =============== +# You can configure behavior via environment variables OR the constants below. +# Examples (zsh): +# # One-off run +# # WEATHER_UNITS can be "metric" or "imperial" +# WEATHER_UNITS=imperial WEATHER_PLACE="Concord, NH" python3 /home/dwilliams/Projects/Weather.py +# +# # Persist in current shell session +# export WEATHER_UNITS=imperial +# export WEATHER_LAT=43.2229 +# export WEATHER_LON=-71.332 +# export WEATHER_PLACE="Concord, NH" +# export WEATHER_TOOLTIP_MARKUP=1 # 1 to enable Pango markup, 0 to disable +# export WEATHER_LOC_ICON="📍" # or "*" for ASCII-only +# +CACHE_DIR = os.path.expanduser("~/.cache") +API_CACHE_PATH = os.path.join(CACHE_DIR, "open_meteo_cache.json") +SIMPLE_TEXT_CACHE_PATH = os.path.join(CACHE_DIR, ".weather_cache") +CACHE_TTL_SECONDS = int(os.getenv("WEATHER_CACHE_TTL", "600")) # default 10 minutes + +# Units: metric or imperial (default metric) +UNITS = os.getenv("WEATHER_UNITS", "metric").strip().lower() # metric|imperial + +# Optional manual coordinates +ENV_LAT = os.getenv("WEATHER_LAT") +ENV_LON = os.getenv("WEATHER_LON") +# Optional manual place override for tooltip +ENV_PLACE = os.getenv("WEATHER_PLACE") +# Manual place name set inside this file. If set (non-empty), this takes top priority. +# Example: MANUAL_PLACE = "Concord, NH, US" +MANUAL_PLACE: Optional[str] = None + +# Location icon in tooltip (default to a standard emoji to avoid missing glyphs) +LOC_ICON = os.getenv("WEATHER_LOC_ICON", "📍") +# Enable/disable Pango markup in tooltip (1/0, true/false) +TOOLTIP_MARKUP = os.getenv("WEATHER_TOOLTIP_MARKUP", "1").lower() not in ("0", "false", "no") +# Optional debug logging to stderr (set WEATHER_DEBUG=1 to enable) +DEBUG = os.getenv("WEATHER_DEBUG", "0").lower() not in ("0", "false", "no") + +# HTTP settings +UA = ( + "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 " + "(KHTML, like Gecko) Chrome/128.0 Safari/537.36" +) +TIMEOUT = 8 -# weather icons -weather_icons = { +SESSION = requests.Session() +SESSION.headers.update({"User-Agent": UA}) + +# =============== Icon and status mapping =============== +# Reuse prior icon set for continuity +WEATHER_ICONS = { "sunnyDay": "󰖙", "clearNight": "󰖔", "cloudyFoggyDay": "", @@ -22,126 +76,469 @@ weather_icons = { "default": "", } +WMO_STATUS = { + 0: "Clear sky", + 1: "Mainly clear", + 2: "Partly cloudy", + 3: "Overcast", + 45: "Fog", + 48: "Depositing rime fog", + 51: "Light drizzle", + 53: "Moderate drizzle", + 55: "Dense drizzle", + 56: "Freezing drizzle", + 57: "Freezing drizzle", + 61: "Light rain", + 63: "Moderate rain", + 65: "Heavy rain", + 66: "Freezing rain", + 67: "Freezing rain", + 71: "Slight snow", + 73: "Moderate snow", + 75: "Heavy snow", + 77: "Snow grains", + 80: "Rain showers", + 81: "Rain showers", + 82: "Violent rain showers", + 85: "Snow showers", + 86: "Heavy snow showers", + 95: "Thunderstorm", + 96: "Thunderstorm w/ hail", + 99: "Thunderstorm w/ hail", +} -# Get current location based on IP address -def get_location(): - response = requests.get("https://ipinfo.io") - data = response.json() - loc = data["loc"].split(",") - return float(loc[0]), float(loc[1]) +def wmo_to_icon(code: int, is_day: int) -> str: + day = bool(is_day) + if code == 0: + return WEATHER_ICONS["sunnyDay" if day else "clearNight"] + if code in (1, 2, 3, 45, 48): + return WEATHER_ICONS["cloudyFoggyDay" if day else "cloudyFoggyNight"] + if code in (51, 53, 55, 61, 63, 65, 80, 81, 82): + return WEATHER_ICONS["rainyDay" if day else "rainyNight"] + if code in (56, 57, 66, 67, 71, 73, 75, 77, 85, 86): + return WEATHER_ICONS["snowyIcyDay" if day else "snowyIcyNight"] + if code in (95, 96, 99): + return WEATHER_ICONS["severe"] + return WEATHER_ICONS["default"] -# Get latitude and longitude -latitude, longitude = get_location() -# Open-Meteo API endpoint -url = f"https://weather.com/en-PH/weather/today/l/{latitude},{longitude}" +def wmo_to_status(code: int) -> str: + return WMO_STATUS.get(code, "Unknown") -# manual location_id -# NOTE: if you want to add manually, make sure you disable def get_location above -# to get your own location_id, go to https://weather.com & search your location. -# once you choose your location, you can see the location_id in the URL(64 chars long hex string) -# like this: https://weather.com/en-PH/weather/today/l/bca47d1099e762a012b9a139c36f30a0b1e647f69c0c4ac28b537e7ae9c1c200 -# location_id = "bca47d1099e762a012b9a139c36f30a0b1e647f69c0c4ac28b537e7ae9c1c200" # TODO -# NOTE to change to deg F, change the URL to your preffered location after weather.com -# Default is English-Philippines with Busan, South Korea as location_id -# get html page -# url = "https://weather.com/en-PH/weather/today/l/" + location_id +# =============== Utilities =============== -html_data = PyQuery(url=url) +def esc(s: Optional[str]) -> str: + return html.escape(s, quote=False) if s else "" -# current temperature -temp = html_data("span[data-testid='TemperatureValue']").eq(0).text() +def log_debug(msg: str) -> None: + if DEBUG: + print(msg, file=sys.stderr) -# current status phrase -status = html_data("div[data-testid='wxPhrase']").text() -status = f"{status[:16]}.." if len(status) > 17 else status +def ensure_cache_dir() -> None: + try: + os.makedirs(CACHE_DIR, exist_ok=True) + except Exception as e: + print(f"Error creating cache dir: {e}", file=sys.stderr) -# status code -# Fix provided by mio-dokuhaki -status_code = html_data("#regionHeader").attr("class").split(" ")[1].split("-")[0] -# status icon -icon = ( - weather_icons[status_code] - if status_code in weather_icons - else weather_icons["default"] -) +def read_api_cache() -> Optional[Dict[str, Any]]: + try: + if not os.path.exists(API_CACHE_PATH): + return None + with open(API_CACHE_PATH, "r", encoding="utf-8") as f: + data = json.load(f) + if (time.time() - data.get("timestamp", 0)) <= CACHE_TTL_SECONDS: + return data + return None + except Exception as e: + print(f"Error reading cache: {e}", file=sys.stderr) + return None -# temperature feels like -temp_feel = html_data( - "div[data-testid='FeelsLikeSection'] > span > span[data-testid='TemperatureValue']" -).text() -temp_feel_text = f"Feels like {temp_feel}c" - -# min-max temperature -temp_min = ( - html_data("div[data-testid='wxData'] > span[data-testid='TemperatureValue']") - .eq(1) - .text() -) -temp_max = ( - html_data("div[data-testid='wxData'] > span[data-testid='TemperatureValue']") - .eq(0) - .text() -) -temp_min_max = f" {temp_min}\t\t {temp_max}" - -# wind speed -wind_speed = str(html_data("span[data-testid='Wind'] > span").text()) -wind_text = f" {wind_speed}" - -# humidity -humidity = html_data("span[data-testid='PercentageValue']").text() -humidity_text = f" {humidity}" - -# visibility -visibility = html_data("span[data-testid='VisibilityValue']").text() -visibility_text = f" {visibility}" - -# air quality index -air_quality_index = html_data("text[data-testid='DonutChartValue']").text() - -# hourly rain prediction -prediction = html_data("section[aria-label='Hourly Forecast']")( - "div[data-testid='SegmentPrecipPercentage'] > span" -).text() -prediction = prediction.replace("Chance of Rain", "") -prediction = f"\n\n (hourly) {prediction}" if len(prediction) > 0 else prediction - -# tooltip text -tooltip_text = str.format( - "\t\t{}\t\t\n{}\n{}\n{}\n\n{}\n{}\n{}{}", - f'{temp}', - f" {icon}", - f"{status}", - f"{temp_feel_text}", - f"{temp_min_max}", - f"{wind_text}\t{humidity_text}", - f"{visibility_text}\tAQI {air_quality_index}", - f" {prediction}", -) -# print waybar module data -out_data = { - "text": f"{icon} {temp}", - "alt": status, - "tooltip": tooltip_text, - "class": status_code, -} -print(json.dumps(out_data)) - -simple_weather = ( - f"{icon} {status}\n" - + f" {temp} ({temp_feel_text})\n" - + f"{wind_text} \n" - + f"{humidity_text} \n" - + f"{visibility_text} AQI{air_quality_index}\n" -) +def write_api_cache(payload: Dict[str, Any]) -> None: + try: + ensure_cache_dir() + payload["timestamp"] = time.time() + with open(API_CACHE_PATH, "w", encoding="utf-8") as f: + json.dump(payload, f) + except Exception as e: + print(f"Error writing API cache: {e}", file=sys.stderr) + + +def write_simple_text_cache(text: str) -> None: + try: + ensure_cache_dir() + with open(SIMPLE_TEXT_CACHE_PATH, "w", encoding="utf-8") as f: + f.write(text) + except Exception as e: + print(f"Error writing simple cache: {e}", file=sys.stderr) + + +def get_coords() -> Tuple[float, float]: + # 1) Explicit env + if ENV_LAT and ENV_LON: + try: + return float(ENV_LAT), float(ENV_LON) + except ValueError: + print("Invalid WEATHER_LAT/WEATHER_LON; falling back to IP geolocation", file=sys.stderr) + + # 2) Try cached coordinates from last successful forecast + try: + cached = read_api_cache() + if cached and isinstance(cached, dict): + fc = cached.get("forecast") or {} + lat = fc.get("latitude") + lon = fc.get("longitude") + if isinstance(lat, (int, float)) and isinstance(lon, (int, float)): + return float(lat), float(lon) + except Exception as e: + print(f"Reading cached coords failed: {e}", file=sys.stderr) + + # 3) IP-based geolocation with multiple providers (prefer ipwho.is, ipapi.co; ipinfo.io as fallback) + # ipwho.is + try: + resp = SESSION.get("https://ipwho.is/", timeout=TIMEOUT) + resp.raise_for_status() + data = resp.json() + if data.get("success"): + lat = data.get("latitude") + lon = data.get("longitude") + if isinstance(lat, (int, float)) and isinstance(lon, (int, float)): + return float(lat), float(lon) + except Exception as e: + print(f"ipwho.is failed: {e}", file=sys.stderr) + + # ipapi.co + try: + resp = SESSION.get("https://ipapi.co/json", timeout=TIMEOUT) + resp.raise_for_status() + data = resp.json() + lat = data.get("latitude") + lon = data.get("longitude") + if isinstance(lat, (int, float)) and isinstance(lon, (int, float)): + return float(lat), float(lon) + except Exception as e: + print(f"ipapi.co failed: {e}", file=sys.stderr) + + # ipinfo.io (fallback) + try: + resp = SESSION.get("https://ipinfo.io/json", timeout=TIMEOUT) + resp.raise_for_status() + data = resp.json() + loc = data.get("loc") + if loc and "," in loc: + lat_s, lon_s = loc.split(",", 1) + return float(lat_s), float(lon_s) + except Exception as e: + print(f"ipinfo.io failed: {e}", file=sys.stderr) + + # 4) Last resort + print("IP geolocation failed: no providers succeeded", file=sys.stderr) + return 0.0, 0.0 + + +def units_params(units: str) -> Dict[str, str]: + if units == "imperial": + return { + "temperature_unit": "fahrenheit", + "wind_speed_unit": "mph", + "precipitation_unit": "inch", + } + # default metric + return { + "temperature_unit": "celsius", + "wind_speed_unit": "kmh", + "precipitation_unit": "mm", + } + + +def format_visibility(meters: Optional[float]) -> str: + if meters is None: + return "" + try: + if UNITS == "imperial": + miles = meters / 1609.344 + return f"{miles:.1f} mi" + else: + km = meters / 1000.0 + return f"{km:.1f} km" + except Exception: + return "" + + +# =============== API Fetching =============== + +def fetch_open_meteo(lat: float, lon: float) -> Dict[str, Any]: + base = "https://api.open-meteo.com/v1/forecast" + params = { + "latitude": lat, + "longitude": lon, + "current": "temperature_2m,apparent_temperature,relative_humidity_2m,wind_speed_10m,wind_direction_10m,weather_code,visibility,precipitation,pressure_msl,is_day", + "hourly": "precipitation_probability", + "daily": "temperature_2m_max,temperature_2m_min", + "timezone": "auto", + } + params.update(units_params(UNITS)) + resp = SESSION.get(base, params=params, timeout=TIMEOUT) + resp.raise_for_status() + return resp.json() + + +def fetch_aqi(lat: float, lon: float) -> Optional[Dict[str, Any]]: + try: + base = "https://air-quality-api.open-meteo.com/v1/air-quality" + params = { + "latitude": lat, + "longitude": lon, + "current": "european_aqi", + "timezone": "auto", + } + resp = SESSION.get(base, params=params, timeout=TIMEOUT) + resp.raise_for_status() + return resp.json() + except Exception as e: + print(f"AQI fetch failed: {e}", file=sys.stderr) + return None + + +def fetch_place(lat: float, lon: float) -> Optional[str]: + """Reverse geocode lat/lon to an approximate place. Tries Nominatim first, then Open-Meteo.""" + lang = os.getenv("WEATHER_LANG", "en") + + # 1) Nominatim (OpenStreetMap) + try: + base = "https://nominatim.openstreetmap.org/reverse" + params = { + "lat": lat, + "lon": lon, + "format": "jsonv2", + "accept-language": lang, + } + headers = {"User-Agent": UA + " Weather.py/1.0"} + resp = SESSION.get(base, params=params, headers=headers, timeout=TIMEOUT) + resp.raise_for_status() + data = resp.json() + address = data.get("address", {}) + name = data.get("name") or address.get("city") or address.get("town") or address.get("village") or address.get("hamlet") + admin1 = address.get("state") + country = address.get("country") + parts = [part for part in [name, admin1, country] if part] + if parts: + return ", ".join(parts) + except Exception as e: + log_debug(f"Reverse geocoding (Nominatim) failed: {e}") + + # 2) Open-Meteo reverse (fallback) + try: + base = "https://geocoding-api.open-meteo.com/v1/reverse" + params = { + "latitude": lat, + "longitude": lon, + "language": lang, + "format": "json", + } + resp = SESSION.get(base, params=params, timeout=TIMEOUT) + resp.raise_for_status() + data = resp.json() + results = data.get("results") or [] + if results: + p = results[0] + name = p.get("name") + admin1 = p.get("admin1") + country = p.get("country") + parts = [part for part in [name, admin1, country] if part] + if parts: + return ", ".join(parts) + except Exception as e: + log_debug(f"Reverse geocoding (Open-Meteo) failed: {e}") + + return None + + +# =============== Build Output =============== + +def safe_get(dct: Dict[str, Any], *keys, default=None): + cur: Any = dct + for k in keys: + if isinstance(cur, dict): + if k not in cur: + return default + cur = cur[k] + elif isinstance(cur, list): + try: + cur = cur[k] # type: ignore[index] + except Exception: + return default + else: + return default + return cur + + +def build_hourly_precip(forecast: Dict[str, Any]) -> str: + try: + times: List[str] = safe_get(forecast, "hourly", "time", default=[]) or [] + probs: List[Optional[float]] = safe_get( + forecast, "hourly", "precipitation_probability", default=[] + ) or [] + cur_time: Optional[str] = safe_get(forecast, "current", "time") + idx = times.index(cur_time) if cur_time in times else 0 + window = probs[idx : idx + 6] + if not window: + return "" + parts = [f"{int(p)}%" if p is not None else "-" for p in window] + return " (next 6h) " + " ".join(parts) + except Exception: + return "" + + +def build_output(lat: float, lon: float, forecast: Dict[str, Any], aqi: Optional[Dict[str, Any]], place: Optional[str] = None) -> Tuple[Dict[str, Any], str]: + cur = forecast.get("current", {}) + cur_units = forecast.get("current_units", {}) + daily = forecast.get("daily", {}) + daily_units = forecast.get("daily_units", {}) + + temp_val = cur.get("temperature_2m") + temp_unit = cur_units.get("temperature_2m", "") + temp_str = f"{int(round(temp_val))}{temp_unit}" if isinstance(temp_val, (int, float)) else "N/A" + + feels_val = cur.get("apparent_temperature") + feels_unit = cur_units.get("apparent_temperature", "") + feels_str = f"Feels like {int(round(feels_val))}{feels_unit}" if isinstance(feels_val, (int, float)) else "" + + is_day = int(cur.get("is_day", 1) or 1) + code = int(cur.get("weather_code", -1) or -1) + icon = wmo_to_icon(code, is_day) + status = wmo_to_status(code) + + # min/max today (index 0) + tmin_val = safe_get(daily, "temperature_2m_min", 0) + tmax_val = safe_get(daily, "temperature_2m_max", 0) + dtemp_unit = daily_units.get("temperature_2m_min", temp_unit) + tmin_str = f"{int(round(tmin_val))}{dtemp_unit}" if isinstance(tmin_val, (int, float)) else "" + tmax_str = f"{int(round(tmax_val))}{dtemp_unit}" if isinstance(tmax_val, (int, float)) else "" + min_max = f" {tmin_str}\t\t {tmax_str}" if tmin_str and tmax_str else "" + + wind_val = cur.get("wind_speed_10m") + wind_unit = cur_units.get("wind_speed_10m", "") + wind_text = f" {int(round(wind_val))}{wind_unit}" if isinstance(wind_val, (int, float)) else "" + + hum_val = cur.get("relative_humidity_2m") + humidity_text = f" {int(hum_val)}%" if isinstance(hum_val, (int, float)) else "" + + vis_val = cur.get("visibility") + visibility_text = f" {format_visibility(vis_val)}" if isinstance(vis_val, (int, float)) else "" + + aqi_val = safe_get(aqi or {}, "current", "european_aqi") + aqi_text = f"AQI {int(aqi_val)}" if isinstance(aqi_val, (int, float)) else "AQI N/A" + + hourly_precip = build_hourly_precip(forecast) + prediction = f"\n\n{hourly_precip}" if hourly_precip else "" + + # Build place string (priority: MANUAL_PLACE > ENV_PLACE > reverse geocode > lat,lon) + place_str = (MANUAL_PLACE or ENV_PLACE or place or f"{lat:.3f}, {lon:.3f}") + location_text = f"{LOC_ICON} {place_str}" + + # Build tooltip (markup or plain) + if TOOLTIP_MARKUP: + # Escape dynamic text to avoid breaking Pango markup + tooltip_text = str.format( + "\t\t{}\t\t\n{}\n{}\n{}\n{}\n\n{}\n{}\n{}{}", + f'{esc(temp_str)}', + f" {icon}", + f"{esc(status)}", + esc(location_text), + f"{esc(feels_str)}" if feels_str else "", + f"{esc(min_max)}" if min_max else "", + f"{esc(wind_text)}\t{esc(humidity_text)}", + f"{esc(visibility_text)}\t{esc(aqi_text)}", + f" {esc(prediction)}" if prediction else "", + ) + else: + lines = [ + f"{icon} {temp_str}", + status, + location_text, + ] + if feels_str: + lines.append(feels_str) + if min_max: + lines.append(min_max) + lines.append(f"{wind_text} {humidity_text}".strip()) + lines.append(f"{visibility_text} {aqi_text}".strip()) + if prediction: + lines.append(hourly_precip) + tooltip_text = "\n".join([ln for ln in lines if ln]) + + out_data = { + "text": f"{icon} {temp_str}", + "alt": status, + "tooltip": tooltip_text, + "class": f"wmo-{code} {'day' if is_day else 'night'}", + } + + simple_weather = ( + f"{icon} {status}\n" + + f" {temp_str} ({feels_str})\n" + + (f"{wind_text} \n" if wind_text else "") + + (f"{humidity_text} \n" if humidity_text else "") + + f"{visibility_text} {aqi_text}\n" + ) + + return out_data, simple_weather + + +def main() -> None: + lat, lon = get_coords() + + # Try cache first + cached = read_api_cache() + if cached and isinstance(cached, dict): + forecast = cached.get("forecast") + aqi = cached.get("aqi") + cached_place = cached.get("place") if isinstance(cached.get("place"), str) else None + place_effective = MANUAL_PLACE or ENV_PLACE or cached_place + try: + out, simple = build_output(lat, lon, forecast, aqi, place_effective) + print(json.dumps(out, ensure_ascii=False)) + write_simple_text_cache(simple) + return + except Exception as e: + print(f"Cached data build failed, refetching: {e}", file=sys.stderr) + + # Fetch fresh + try: + forecast = fetch_open_meteo(lat, lon) + aqi = fetch_aqi(lat, lon) + # Use manual/env place if provided; otherwise reverse geocode + place_effective = MANUAL_PLACE or ENV_PLACE or fetch_place(lat, lon) + write_api_cache({"forecast": forecast, "aqi": aqi, "place": place_effective}) + out, simple = build_output(lat, lon, forecast, aqi, place_effective) + print(json.dumps(out, ensure_ascii=False)) + write_simple_text_cache(simple) + except Exception as e: + print(f"Open-Meteo fetch failed: {e}", file=sys.stderr) + # Last resort: try stale cache without TTL + try: + if os.path.exists(API_CACHE_PATH): + with open(API_CACHE_PATH, "r", encoding="utf-8") as f: + stale = json.load(f) + out, simple = build_output(lat, lon, stale.get("forecast", {}), stale.get("aqi"), stale.get("place") if isinstance(stale.get("place"), str) else None) + print(json.dumps(out, ensure_ascii=False)) + write_simple_text_cache(simple) + return + except Exception as e2: + print(f"Failed to use stale cache: {e2}", file=sys.stderr) + # Fallback minimal output + fallback = { + "text": f"{WEATHER_ICONS['default']} N/A", + "alt": "Unavailable", + "tooltip": "Weather unavailable", + "class": "unavailable", + } + print(json.dumps(fallback, ensure_ascii=False)) + -try: - with open(os.path.expanduser("~/.cache/.weather_cache"), "w") as file: - file.write(simple_weather) -except Exception as e: - print(f"Error writing to cache: {e}") +if __name__ == "__main__": + main() -- cgit v1.2.3 From 40f124f2321a6af20bf4b7e2788195bebb1c0421 Mon Sep 17 00:00:00 2001 From: Don Williams Date: Sun, 12 Oct 2025 06:30:14 -0400 Subject: Added Groups & keybinds from Matt @ The Linux cast On branch ddubs-hyprsunset Your branch is up to date with 'origin/ddubs-hyprsunset'. Changes to be committed: modified: config/hypr/UserConfigs/UserKeybinds.conf modified: config/hypr/UserConfigs/UserSettings.conf --- config/hypr/UserConfigs/UserKeybinds.conf | 11 +++++++++++ config/hypr/UserConfigs/UserSettings.conf | 23 +++++++++++++++++++++++ 2 files changed, 34 insertions(+) (limited to 'config/hypr') diff --git a/config/hypr/UserConfigs/UserKeybinds.conf b/config/hypr/UserConfigs/UserKeybinds.conf index d8a1d52e..2f047093 100644 --- a/config/hypr/UserConfigs/UserKeybinds.conf +++ b/config/hypr/UserConfigs/UserKeybinds.conf @@ -78,6 +78,17 @@ bind = $mainMod CTRL, F10, movecurrentworkspacetomonitor, r #move current worksp bind = $mainMod CTRL, F11, movecurrentworkspacetomonitor, u #move current workspace to UP monitor bind = $mainMod CTRL, F12, movecurrentworkspacetomonitor, d #move current workspace to DOWN monitor +# Groups +bind = $mainMod, U, togglegroup, # Toggle group +bind = ALT, H, changegroupactive, b # Change group active +bind = ALT, L, changegroupactive, f # Change group active +bind = $mainMod, I, lockactivegroup, toggle # Lock active group +bind = $mainMod ALT, U, moveoutofgroup, # Move out of group +bind = $mainMod ALT, H, moveintogroup, l # Move into group left +bind = $mainMod ALT, L, moveintogroup, r # Move into group right +bind = $mainMod ALT, K, moveintogroup, u # Moving into group up +bind = $mainMod ALT, J, moveintogroup, d # Moving into group down + # For passthrough keyboard into a VM # bind = $mainMod ALT, P, submap, passthru #submap = passthru diff --git a/config/hypr/UserConfigs/UserSettings.conf b/config/hypr/UserConfigs/UserSettings.conf index 325d24f7..b9714a44 100644 --- a/config/hypr/UserConfigs/UserSettings.conf +++ b/config/hypr/UserConfigs/UserSettings.conf @@ -63,6 +63,29 @@ input { } } +group { + col.border_active = $color2 $color4 45deg + col.border_inactive = $color10 + + groupbar { + enabled = true + font_family = JetBrainsMono Nerd Font + font_size = 12 + gradients = true + height = 18 + priority = 3 + render_titles = true + scrolling = true + text_color = $foreground + + col.active = $color3 + col.inactive = $background + col.locked_active = $color6 + col.locked_inactive = $color8 + } +} + + gestures { gesture = 3, horizontal, workspace workspace_swipe_distance = 500 -- cgit v1.2.3 From 4d2ef003eabfd31591a0a04b17be7e8e5c869c0e Mon Sep 17 00:00:00 2001 From: Don Williams Date: Sun, 12 Oct 2025 06:50:14 -0400 Subject: Removed group feature --- config/hypr/UserConfigs/UserKeybinds.conf | 10 ---------- config/hypr/UserConfigs/UserSettings.conf | 22 ---------------------- 2 files changed, 32 deletions(-) (limited to 'config/hypr') diff --git a/config/hypr/UserConfigs/UserKeybinds.conf b/config/hypr/UserConfigs/UserKeybinds.conf index 2f047093..4d35a9c9 100644 --- a/config/hypr/UserConfigs/UserKeybinds.conf +++ b/config/hypr/UserConfigs/UserKeybinds.conf @@ -78,16 +78,6 @@ bind = $mainMod CTRL, F10, movecurrentworkspacetomonitor, r #move current worksp bind = $mainMod CTRL, F11, movecurrentworkspacetomonitor, u #move current workspace to UP monitor bind = $mainMod CTRL, F12, movecurrentworkspacetomonitor, d #move current workspace to DOWN monitor -# Groups -bind = $mainMod, U, togglegroup, # Toggle group -bind = ALT, H, changegroupactive, b # Change group active -bind = ALT, L, changegroupactive, f # Change group active -bind = $mainMod, I, lockactivegroup, toggle # Lock active group -bind = $mainMod ALT, U, moveoutofgroup, # Move out of group -bind = $mainMod ALT, H, moveintogroup, l # Move into group left -bind = $mainMod ALT, L, moveintogroup, r # Move into group right -bind = $mainMod ALT, K, moveintogroup, u # Moving into group up -bind = $mainMod ALT, J, moveintogroup, d # Moving into group down # For passthrough keyboard into a VM # bind = $mainMod ALT, P, submap, passthru diff --git a/config/hypr/UserConfigs/UserSettings.conf b/config/hypr/UserConfigs/UserSettings.conf index b9714a44..f81ccc6a 100644 --- a/config/hypr/UserConfigs/UserSettings.conf +++ b/config/hypr/UserConfigs/UserSettings.conf @@ -63,28 +63,6 @@ input { } } -group { - col.border_active = $color2 $color4 45deg - col.border_inactive = $color10 - - groupbar { - enabled = true - font_family = JetBrainsMono Nerd Font - font_size = 12 - gradients = true - height = 18 - priority = 3 - render_titles = true - scrolling = true - text_color = $foreground - - col.active = $color3 - col.inactive = $background - col.locked_active = $color6 - col.locked_inactive = $color8 - } -} - gestures { gesture = 3, horizontal, workspace -- cgit v1.2.3 From 2e24193c7322d29dbdd639b82251b808b0851043 Mon Sep 17 00:00:00 2001 From: Don Williams Date: Mon, 13 Oct 2025 17:24:39 -0400 Subject: Removed v2.3.16 file in prep for release --- config/hypr/v2.3.16 | 5 ----- 1 file changed, 5 deletions(-) delete mode 100644 config/hypr/v2.3.16 (limited to 'config/hypr') diff --git a/config/hypr/v2.3.16 b/config/hypr/v2.3.16 deleted file mode 100644 index 31b3414d..00000000 --- a/config/hypr/v2.3.16 +++ /dev/null @@ -1,5 +0,0 @@ -### https://github.com/JaKooLit ### -## https://github.com/JaKooLit/Hyprland-Dots -## This is to have a reference of which version would be - -## note that this will always be higher than the released versions \ No newline at end of file -- cgit v1.2.3