aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--config/hypr/UserConfigs/Startup_Apps.conf2
-rwxr-xr-xconfig/hypr/UserScripts/Weather.py57
-rwxr-xr-xconfig/hypr/UserScripts/Weather.sh212
-rwxr-xr-xconfig/hypr/UserScripts/weatherWrap.sh33
-rw-r--r--config/hypr/hypridle.conf4
-rwxr-xr-xconfig/hypr/scripts/LockScreen.sh3
6 files changed, 291 insertions, 20 deletions
diff --git a/config/hypr/UserConfigs/Startup_Apps.conf b/config/hypr/UserConfigs/Startup_Apps.conf
index 976208e9..c40d0b0a 100644
--- a/config/hypr/UserConfigs/Startup_Apps.conf
+++ b/config/hypr/UserConfigs/Startup_Apps.conf
@@ -48,7 +48,7 @@ exec-once = $UserScripts/RainbowBorders.sh
exec-once = hypridle
# Weather script to populate cache on startup
-exec-once = $UserScripts/Weather.sh
+exec-once = $UserScripts/weatherWrap.sh
# Here are list of features available but disabled by default
diff --git a/config/hypr/UserScripts/Weather.py b/config/hypr/UserScripts/Weather.py
index 4c64221f..1a7380cf 100755
--- a/config/hypr/UserScripts/Weather.py
+++ b/config/hypr/UserScripts/Weather.py
@@ -65,11 +65,12 @@ 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
+# Optional manual place override for tooltip (and optional forward geocoding)
ENV_PLACE = os.getenv("WEATHER_PLACE")
-# Manual place name set inside this file. If set (non-empty), this takes top priority.
+# Manual place name set inside this file. If set (non-empty), this takes top priority for display
+# and, if coordinates are not provided, will be used to geocode latitude/longitude.
# Example: MANUAL_PLACE = "Concord, NH, US"
-MANUAL_PLACE: Optional[str] = None
+MANUAL_PLACE: Optional[str] = "" #Set your city HERE
# Location icon in tooltip (default to a standard emoji to avoid missing glyphs)
LOC_ICON = os.getenv("WEATHER_LOC_ICON", "๐Ÿ“")
@@ -324,23 +325,58 @@ def get_coords_from_ipinfo() -> Optional[Tuple[float, float]]:
return None
+def get_coords_from_place_name(name: str) -> Optional[Tuple[float, float]]:
+ """Forward geocode a place name to coordinates using Open-Meteo Geocoding API.
+
+ Returns (lat, lon) if found, else None.
+ """
+ try:
+ base = "https://geocoding-api.open-meteo.com/v1/search"
+ params: Dict[str, Union[str, float]] = {
+ "name": name,
+ "count": 1,
+ "language": os.getenv("WEATHER_LANG", "en"),
+ "format": "json",
+ }
+ resp = SESSION.get(base, params=params, timeout=TIMEOUT)
+ resp.raise_for_status()
+ data = ensure_dict(resp.json())
+ results = ensure_list(data.get("results"))
+ if results:
+ p = ensure_dict(results[0])
+ lat = coerce_float(p.get("latitude"))
+ lon = coerce_float(p.get("longitude"))
+ if lat is not None and lon is not None:
+ return float(lat), float(lon)
+ except Exception as e:
+ print(f"Place geocoding failed: {e}", file=sys.stderr)
+ return None
+
+
def get_coords() -> Tuple[float, float]:
- # 1) Explicit env
+ # 1) Explicit env coordinates
coords = get_coords_from_env()
if coords:
return coords
- # 2) Try cached coordinates
+ # 2) Forward geocode from a specified place name (manual takes precedence over env)
+ place_name = (MANUAL_PLACE or "").strip() or (ENV_PLACE or "").strip()
+ if place_name:
+ coords = get_coords_from_place_name(place_name)
+ if coords:
+ return coords
+
+ # 3) Try cached coordinates
coords = get_coords_from_cache()
if coords:
return coords
- # 3) IP-based geolocation
+ # 4) IP-based geolocation
coords = get_coords_from_ipwho() or get_coords_from_ipapi() or get_coords_from_ipinfo()
if coords:
return coords
- # 4) Last resort
+ # 5) Last resort
print("IP geolocation failed: no providers succeeded", file=sys.stderr)
return 0.0, 0.0
@@ -778,6 +814,13 @@ def try_cached_weather(lat: float, lon: float) -> Optional[Tuple[Dict[str, str],
aqi = cast(Optional[Dict[str, Any]], cached.get("aqi"))
place_val = cached.get("place")
cached_place = place_val if isinstance(place_val, str) else None
+ # Ensure the cached forecast corresponds to the requested lat/lon
+ fc = ensure_dict(cached.get("forecast"))
+ c_lat = coerce_float(safe_get(fc, "latitude"))
+ c_lon = coerce_float(safe_get(fc, "longitude"))
+ if c_lat is not None and c_lon is not None:
+ if abs(c_lat - lat) > 0.1 or abs(c_lon - lon) > 0.1:
+ return None # force fresh fetch for new location
try:
return build_output(Location(lat, lon, cached_place), forecast, aqi)
except Exception as e:
diff --git a/config/hypr/UserScripts/Weather.sh b/config/hypr/UserScripts/Weather.sh
index 0540e51d..b69662e6 100755
--- a/config/hypr/UserScripts/Weather.sh
+++ b/config/hypr/UserScripts/Weather.sh
@@ -1,15 +1,209 @@
#!/bin/bash
# /* ---- ๐Ÿ’ซ https://github.com/JaKooLit ๐Ÿ’ซ ---- */ ##
-# weather info using Python script with Open-Meteo APIs
+# weather info from wttr. https://github.com/chubin/wttr.in
+# Remember to add city
-if ! command -v python3 >/dev/null 2>&1; then
- echo "python3 not found in PATH" >&2
- exit 127
+city=""
+
+
+# if city is blank, use https://ipapi.co/json to get location from IP
+if [ -z "$city" ]; then
+ city=$(curl -fsS https://ipapi.co/json | grep city | cut -f4 -d'"')
+fi
+
+
+# URL-encode city for safe use in URLs
+encoded_city="$city"
+if command -v python3 >/dev/null 2>&1; then
+ encoded_city=$(python3 -c 'import urllib.parse,sys; print(urllib.parse.quote(sys.argv[1]))' "$city")
+elif command -v jq >/dev/null 2>&1; then
+ encoded_city=$(printf '%s' "$city" | jq -sRr @uri)
+else
+ # Minimal fallback: encode a few common special characters
+ encoded_city=$(printf '%s' "$city" | sed -e 's/ /%20/g' -e 's/&/%26/g' -e 's/?/%3F/g' -e 's/#/%23/g')
fi
-python3 "$(dirname "$0")/Weather.py" "$@"
-exit_code=$?
-if [ "$exit_code" -ne 0 ]; then
- echo "Failed to run Weather.py" >&2
+
+cachedir="$HOME/.cache/rbn"
+# Include city and arg in cache key so changing city invalidates old cache
+cache_key="${city}_${1}"
+# Sanitize cache key to avoid problematic characters in filename
+safe_key=$(printf '%s' "$cache_key" | tr -c '[:alnum:]_-' '_')
+cachefile=${0##*/}-$safe_key
+
+if [ ! -d "$cachedir" ]; then
+ mkdir -p "$cachedir"
+fi
+
+if [ ! -f "$cachedir/$cachefile" ]; then
+ touch "$cachedir/$cachefile"
+fi
+
+# Save current IFS
+SAVEIFS=$IFS
+# Change IFS to new line.
+IFS=$'\n'
+
+file="$cachedir/$cachefile"
+# Portable file mtime retrieval (GNU/BSD):
+# - GNU: stat -c %Y <file>
+# - BSD/macOS: stat -f %m <file>
+mtime=$(stat -c %Y "$file" 2>/dev/null || stat -f %m "$file" 2>/dev/null || echo 0)
+now=$(date +%s)
+cacheage=$(( now - mtime ))
+if [ $cacheage -gt 1740 ] || [ ! -s "$cachedir/$cachefile" ]; then
+ # Prefer structured format for reliable parsing (3 lines: location, condition, temperature)
+ mapfile -t sdata < <(curl -fsS "https://wttr.in/${encoded_city}?format=%25l%0A%25C%0A%25t&lang=en" 2>/dev/null || true)
+ if [ ${#sdata[@]} -ge 3 ]; then
+ printf "%s\n" "${sdata[0]}" > "$cachedir/$cachefile"
+ printf "%s\n" "${sdata[1]}" >> "$cachedir/$cachefile"
+ printf "%s\n" "${sdata[2]}" >> "$cachedir/$cachefile"
+ else
+ # Try fetching each field separately if combined format is flaky
+ loc=$(curl -fsS "https://wttr.in/${encoded_city}?format=%25l&lang=en" 2>/dev/null || true)
+ cond_only=$(curl -fsS "https://wttr.in/${encoded_city}?format=%25C&lang=en" 2>/dev/null || true)
+ temp_only=$(curl -fsS "https://wttr.in/${encoded_city}?format=%25t" 2>/dev/null || true)
+ if [ -n "$loc" ] && [ -n "$cond_only" ] && [ -n "$temp_only" ]; then
+ printf "%s\n" "$loc" > "$cachedir/$cachefile"
+ printf "%s\n" "$cond_only" >> "$cachedir/$cachefile"
+ printf "%s\n" "$temp_only" >> "$cachedir/$cachefile"
+ else
+ # Fallback: try ASCII output and extract best-effort fields
+ url="https://en.wttr.in/${encoded_city}?1"
+ mapfile -t data < <(curl -fsS "$url" 2>/dev/null || true)
+ if [ ${#data[@]} -ge 3 ] && ! echo "${data[0]}" | grep -qi 'not found\|unknown location'; then
+ loc=$(echo "${data[0]}" | sed -E 's/^.*: *//')
+ # Attempt to pull condition and temperature hints from nearby lines
+ cond=$(echo "${data[2]}" | sed -E 's/^.{0,15}//; s/^\s+//')
+ temp=$(printf "%s\n" "${data[@]}" | grep -Eo '\+?-?[0-9]+(\([^)]+\))? ?ยฐ?[CF]' | head -n1)
+ # Only write if we have at least location and something else meaningful
+ if [ -n "$loc" ] && { [ -n "$cond" ] || [ -n "$temp" ]; }; then
+ printf "%s\n" "$loc" > "$cachedir/$cachefile"
+ printf "%s\n" "${cond:-Unknown}" >> "$cachedir/$cachefile"
+ printf "%s\n" "${temp:-N/A}" >> "$cachedir/$cachefile"
+ fi
+ fi
+ fi
+ fi
+fi
+
+# Read cache robustly (line-wise)
+mapfile -t weather < "$cachedir/$cachefile"
+
+# If cache is still empty or invalid, emit a single error JSON and exit to avoid double-prints
+if [ ${#weather[@]} -lt 3 ] || ! echo "${weather[2]}" | grep -qE '[-+0-9].*ยฐ'; then
+ # Last-chance: try live structured fetch and populate cache and runtime weather
+ mapfile -t sdata < <(curl -fsS "https://wttr.in/${encoded_city}?format=%25l%0A%25C%0A%25t&lang=en" 2>/dev/null || true)
+ if [ ${#sdata[@]} -ge 3 ]; then
+ weather=("${sdata[@]}")
+ printf "%s\n" "${sdata[0]}" > "$cachedir/$cachefile"
+ printf "%s\n" "${sdata[1]}" >> "$cachedir/$cachefile"
+ printf "%s\n" "${sdata[2]}" >> "$cachedir/$cachefile"
+ else
+ loc=$(curl -fsS "https://wttr.in/${encoded_city}?format=%25l&lang=en" 2>/dev/null || true)
+ cond_only=$(curl -fsS "https://wttr.in/${encoded_city}?format=%25C&lang=en" 2>/dev/null || true)
+ temp_only=$(curl -fsS "https://wttr.in/${encoded_city}?format=%25t" 2>/dev/null || true)
+ if [ -n "$loc" ] && [ -n "$cond_only" ] && [ -n "$temp_only" ]; then
+ weather=("$loc" "$cond_only" "$temp_only")
+ printf "%s\n" "$loc" > "$cachedir/$cachefile"
+ printf "%s\n" "$cond_only" >> "$cachedir/$cachefile"
+ printf "%s\n" "$temp_only" >> "$cachedir/$cachefile"
+ else
+ echo -e "{\"text\":\"\uf06a\", \"alt\":\"\", \"tooltip\":\": \"}"
+ exit 1
+ fi
+ fi
fi
-exit "$exit_code" \ No newline at end of file
+
+# Restore IFSClear
+IFS=$SAVEIFS
+
+temperature=$(echo "${weather[2]}" | sed -E 's/([[:digit:]]+)\.\./\1 to /g')
+
+#echo ${weather[1]##*,}
+
+# https://fontawesome.com/icons?s=solid&c=weather
+# Normalize condition string for matching
+cond_key=$(echo "${weather[1]##*,}" | tr '[:upper:]' '[:lower:]' | sed -E 's/^\s+//; s/\s+$//')
+case "$cond_key" in
+"clear" | "sunny")
+ condition="๎Œ"
+ ;;
+"partly cloudy")
+ condition="๓ฐ–•"
+ ;;
+"cloudy")
+ condition="๎Œ’"
+ ;;
+"overcast")
+ condition="๎ŒŒ"
+ ;;
+"fog" | "freezing fog")
+ condition="๎Œ“"
+ ;;
+"patchy rain possible" | "patchy light drizzle" | "light drizzle" | "patchy light rain" | "light rain" | "light rain shower" | "mist" | "rain" | "patchy rain nearby")
+ condition="๓ฐผณ"
+ ;;
+"moderate rain at times" | "moderate rain" | "heavy rain at times" | "heavy rain" | "moderate or heavy rain shower" | "torrential rain shower" | "rain shower")
+ condition="๎ˆน"
+ ;;
+"patchy snow possible" | "patchy sleet possible" | "patchy freezing drizzle possible" | "freezing drizzle" | "heavy freezing drizzle" | "light freezing rain" | "moderate or heavy freezing rain" | "light sleet" | "ice pellets" | "light sleet showers" | "moderate or heavy sleet showers")
+ condition="๓ฐผด"
+ ;;
+"blowing snow" | "moderate or heavy sleet" | "patchy light snow" | "light snow" | "light snow showers")
+ condition="๓ฐ™ฟ"
+ ;;
+"blizzard" | "patchy moderate snow" | "moderate snow" | "patchy heavy snow" | "heavy snow" | "moderate or heavy snow with thunder" | "moderate or heavy snow showers")
+ condition="๎ž"
+ ;;
+"thundery outbreaks possible" | "patchy light rain with thunder" | "moderate or heavy rain with thunder" | "patchy light snow with thunder")
+ condition="๎Œ"
+ ;;
+*)
+ condition="๏ช"
+ ;;
+esac
+
+# If still unknown, try substring heuristics to pick a reasonable icon
+if [ "$condition" = "๏ช" ]; then
+ if echo "$cond_key" | grep -q "rain\|drizzle\|shower"; then
+ condition="๓ฐผณ"
+ elif echo "$cond_key" | grep -q "heavy rain\|torrential"; then
+ condition="๎ˆน"
+ elif echo "$cond_key" | grep -q "snow"; then
+ condition="๓ฐ™ฟ"
+ elif echo "$cond_key" | grep -q "sleet\|freezing\|ice"; then
+ condition="๓ฐผด"
+ elif echo "$cond_key" | grep -q "thunder"; then
+ condition="๎Œ"
+ elif echo "$cond_key" | grep -q "overcast"; then
+ condition="๎ŒŒ"
+ elif echo "$cond_key" | grep -q "cloud"; then
+ condition="๎Œ’"
+ elif echo "$cond_key" | grep -q "sunny\|clear"; then
+ condition="๎Œ"
+ fi
+fi
+
+#echo $temp $condition
+
+# Ensure temperature has a value; if empty, keep whatever is in weather[2] or N/A
+if [ -z "$temperature" ]; then
+ temperature="${weather[2]:-N/A}"
+fi
+
+cond_disp=$(echo "${weather[1]}" | sed -E 's/^\s+//; s/\s+$//')
+
+# Escape strings for safe JSON embedding (escape backslashes and double quotes)
+json_escape() {
+ printf '%s' "$1" | sed -e 's/\\/\\\\/g' -e 's/\"/\\\"/g'
+}
+
+text_json=$(json_escape "$temperature $condition")
+alt_json=$(json_escape "${weather[0]}")
+tooltip_json=$(json_escape "${weather[0]}: $temperature $cond_disp")
+
+printf '{"text":"%s", "alt":"%s", "tooltip":"%s"}\n' "$text_json" "$alt_json" "$tooltip_json"
+
+# Write a two-line cache with an actual newline between lines
+printf '๏‹‰ %s \n%s %s\n' "$temperature" "$condition" "${weather[1]}" > "$HOME/.cache/.weather_cache" \ No newline at end of file
diff --git a/config/hypr/UserScripts/weatherWrap.sh b/config/hypr/UserScripts/weatherWrap.sh
new file mode 100755
index 00000000..4c9a16dc
--- /dev/null
+++ b/config/hypr/UserScripts/weatherWrap.sh
@@ -0,0 +1,33 @@
+#!/bin/bash
+# /* ---- ๐Ÿ’ซ https://github.com/JaKooLit ๐Ÿ’ซ ---- */ ##
+# Weather entrypoint: prefer Python (Openโ€‘Meteo), fallback to legacy Bash (wttr.in)
+
+SCRIPT_DIR="$(dirname "$0")"
+PY_SCRIPT="$SCRIPT_DIR/Weather.py"
+BASH_FALLBACK="$SCRIPT_DIR/Weather.sh"
+
+run_fallback() {
+ if [ -f "$BASH_FALLBACK" ]; then
+ # Invoke via bash to avoid requiring +x and ensure consistent shell
+ bash "$BASH_FALLBACK" "$@"
+ return $?
+ else
+ echo "Weather fallback not found: $BASH_FALLBACK" >&2
+ return 127
+ fi
+}
+
+if command -v python3 >/dev/null 2>&1; then
+ python3 "$PY_SCRIPT" "$@"
+ exit_code=$?
+ if [ "$exit_code" -eq 0 ]; then
+ exit 0
+ fi
+ echo "Weather.py failed with code $exit_code โ€” falling back to Weather.sh" >&2
+ run_fallback "$@"
+ exit $?
+else
+ echo "python3 not found in PATH โ€” falling back to Weather.sh" >&2
+ run_fallback "$@"
+ exit $?
+fi \ No newline at end of file
diff --git a/config/hypr/hypridle.conf b/config/hypr/hypridle.conf
index 4b8cd7e2..c1343926 100644
--- a/config/hypr/hypridle.conf
+++ b/config/hypr/hypridle.conf
@@ -1,7 +1,5 @@
-# /* ---- ๐Ÿ’ซ https://github.com/JaKooLit ๐Ÿ’ซ ---- */ #
# Hypridle
-# Original config submitted by https://github.com/SherLock707
-
+
$iDIR="$HOME/.config/swaync/images/ja.png"
general {
diff --git a/config/hypr/scripts/LockScreen.sh b/config/hypr/scripts/LockScreen.sh
index 5e799181..17239406 100755
--- a/config/hypr/scripts/LockScreen.sh
+++ b/config/hypr/scripts/LockScreen.sh
@@ -4,4 +4,7 @@
# For Hyprlock
#pidof hyprlock || hyprlock -q
+# Ensure weather cache is up-to-date before locking (Waybar/lockscreen readers)
+bash "$HOME/.secret/Hyprland-Dots/config/hypr/UserScripts/weatherWrap.sh" >/dev/null 2>&1
+
loginctl lock-session \ No newline at end of file
send patches to the email below
yukais@pinapelz.com
include the subject [PATCH repo_name]
pinapelz.com
homepage