diff options
Diffstat (limited to 'config/hypr')
| -rw-r--r-- | config/hypr/UserConfigs/Startup_Apps.conf | 4 | ||||
| -rw-r--r-- | config/hypr/UserConfigs/UserKeybinds.conf | 5 | ||||
| -rw-r--r-- | config/hypr/UserConfigs/UserSettings.conf | 1 | ||||
| -rw-r--r-- | config/hypr/UserConfigs/WindowRules.conf | 1 | ||||
| -rwxr-xr-x | config/hypr/UserScripts/WallpaperEffects.sh | 58 | ||||
| -rwxr-xr-x | config/hypr/UserScripts/WallpaperSelect.sh | 22 | ||||
| -rwxr-xr-x | config/hypr/UserScripts/Weather.py | 591 | ||||
| -rwxr-xr-x | config/hypr/scripts/Hyprsunset.sh | 99 | ||||
| -rwxr-xr-x | config/hypr/scripts/MediaCtrl.sh | 65 | ||||
| -rw-r--r-- | config/hypr/scripts/sddm_wallpaper.sh | 23 | ||||
| -rw-r--r-- | config/hypr/v2.3.16 | 5 |
11 files changed, 216 insertions, 658 deletions
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 diff --git a/config/hypr/UserConfigs/UserKeybinds.conf b/config/hypr/UserConfigs/UserKeybinds.conf index 2f0e808f..4d35a9c9 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 @@ -74,6 +78,7 @@ 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 + # 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..f81ccc6a 100644 --- a/config/hypr/UserConfigs/UserSettings.conf +++ b/config/hypr/UserConfigs/UserSettings.conf @@ -63,6 +63,7 @@ input { } } + gestures { gesture = 3, horizontal, workspace workspace_swipe_distance = 500 diff --git a/config/hypr/UserConfigs/WindowRules.conf b/config/hypr/UserConfigs/WindowRules.conf index 16dc48a5..d6959dc4 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* 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 + # 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 - # 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 + 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 - exec $SCRIPTSDIR/sddm_wallpaper.sh --effects - + 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/UserScripts/Weather.py b/config/hypr/UserScripts/Weather.py index a71fe8ca..ca1d5281 100755 --- a/config/hypr/UserScripts/Weather.py +++ b/config/hypr/UserScripts/Weather.py @@ -9,7 +9,6 @@ import sys import time import html from typing import Any, Dict, List, Optional, Tuple -from datetime import datetime import requests @@ -18,7 +17,7 @@ import requests # Examples (zsh): # # One-off run # # WEATHER_UNITS can be "metric" or "imperial" -# WEATHER_UNITS=imperial WEATHER_PLACE="Concord, NH" python3 ~/.config/hypr/UserScripts/Weather.py +# WEATHER_UNITS=imperial WEATHER_PLACE="Concord, NH" python3 /home/dwilliams/Projects/Weather.py # # # Persist in current shell session # export WEATHER_UNITS=imperial @@ -277,594 +276,6 @@ def fetch_open_meteo(lat: float, lon: float) -> Dict[str, Any]: "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,weather_code", - "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) - - unavailable = False - if code == -1: - try: - times: List[str] = safe_get(forecast, "hourly", "time", default=[]) or [] - codes: List[Optional[int]] = safe_get(forecast, "hourly", "weather_code", default=[]) or [] - cur_time: Optional[str] = safe_get(forecast, "current", "time") - - idx = 0 - if cur_time and times: - try: - ct = datetime.fromisoformat(cur_time) - diffs = [] - for t in times: - try: - diffs.append(abs((datetime.fromisoformat(t) - ct).total_seconds())) - except Exception: - diffs.append(float("inf")) - idx = min(range(len(diffs)), key=lambda i: diffs[i]) if diffs else 0 - except Exception: - idx = times.index(cur_time) if cur_time in times else 0 - - hcode = None - if isinstance(codes, list) and codes: - if idx < len(codes) and isinstance(codes[idx], (int, float)): - hcode = int(codes[idx]) - else: - for c in codes: - if isinstance(c, (int, float)): - hcode = int(c) - break - if isinstance(hcode, int): - code = hcode - log_debug(f"Fallback hourly weather_code used: code={code} idx={idx} cur_time={cur_time}") - except Exception as e: - log_debug(f"Hourly code fallback failed: {e}") - - if not isinstance(code, int) or code < 0: - unavailable = True - log_debug("Weather code invalid; setting status to 'Condition Unavailable'") - - if unavailable: - icon = WEATHER_ICONS["default"] - status = "Condition Unavailable" - code_for_class = "unavailable" - else: - icon = wmo_to_icon(code, is_day) - status = wmo_to_status(code) - code_for_class = f"wmo-{code} {'day' if is_day else 'night'}" - - # 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'<span size="xx-large">{esc(temp_str)}</span>', - f"<big> {icon}</big>", - f"<b>{esc(status)}</b>", - esc(location_text), - f"<small>{esc(feels_str)}</small>" if feels_str else "", - f"<b>{esc(min_max)}</b>" if min_max else "", - f"{esc(wind_text)}\t{esc(humidity_text)}", - f"{esc(visibility_text)}\t{esc(aqi_text)}", - f"<i> {esc(prediction)}</i>" 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": code_for_class, - } - - 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)) - - -if __name__ == "__main__": - main() -status = html_data("div[data-testid='wxPhrase']").text() -status = f"{status[:16]}.." if len(status) > 17 else status - -# status code -status_code = html_data("#regionHeader").attr("class").split(" ")[2].split("-")[2] - -# status icon -icon = ( - weather_icons[status_code] - if status_code in weather_icons - else weather_icons["default"] -) - -# 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 = html_data("span[data-testid='Wind']").text().split("\n")[1] -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'<span size="xx-large">{temp}</span>', - f"<big> {icon}</big>", - f"<b>{status}</b>", - f"<small>{temp_feel_text}</small>", - f"<b>{temp_min_max}</b>", - f"{wind_text}\t{humidity_text}", - f"{visibility_text}\tAQI {air_quality_index}", - f"<i> {prediction}</i>", -) - -# print waybar module data -out_data = { - "text": f"{icon} {temp}", - "alt": status, - "tooltip": tooltip_text, - "class": status_code, -======= -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", ->>>>>>> 2a5a7c5 (Weather.py: switch to Open-Meteo; add caching, reverse geocoding, robust geolocation, and config options) -} - - -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"] - - -def wmo_to_status(code: int) -> str: - return WMO_STATUS.get(code, "Unknown") - - -# =============== Utilities =============== - -def esc(s: Optional[str]) -> str: - return html.escape(s, quote=False) if s else "" - -def log_debug(msg: str) -> None: - if DEBUG: - print(msg, file=sys.stderr) - -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) - - -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 - - -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", diff --git a/config/hypr/scripts/Hyprsunset.sh b/config/hypr/scripts/Hyprsunset.sh new file mode 100755 index 00000000..c7c4b395 --- /dev/null +++ b/config/hypr/scripts/Hyprsunset.sh @@ -0,0 +1,99 @@ +#!/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() { + # universally available sun symbol + printf "☀" +} + +icon_on() { + case "$ICON_MODE" in + sunset) + # sunset emoji (falls back to tofu if no emoji font) + printf "🌇" + ;; + blue) + # no color in text; rely on CSS .on to style if desired + printf "☀" + ;; + *) + printf "☀" + ;; + esac +} + +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 + 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" + 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 +} + +cmd_status() { + ensure_state + # 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="<span size='18pt'>$(icon_on)</span>" + cls="on" + tip="Night light on @ ${TARGET_TEMP}K" + else + txt="<span size='16pt'>$(icon_off)</span>" + 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/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 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 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 |
