fixes
This commit is contained in:
@@ -225,7 +225,7 @@ in
|
|||||||
halign = "center";
|
halign = "center";
|
||||||
valign = "center";
|
valign = "center";
|
||||||
}
|
}
|
||||||
#weather
|
# weather
|
||||||
{
|
{
|
||||||
monitor = cfg.primaryDisplay;
|
monitor = cfg.primaryDisplay;
|
||||||
text = "cmd[update:30000] waybar-weather --hyprlock";
|
text = "cmd[update:30000] waybar-weather --hyprlock";
|
||||||
@@ -236,6 +236,17 @@ in
|
|||||||
halign = "right";
|
halign = "right";
|
||||||
valign = "bottom";
|
valign = "bottom";
|
||||||
}
|
}
|
||||||
|
# media
|
||||||
|
{
|
||||||
|
monitor = cfg.primaryDisplay;
|
||||||
|
text = "cmd[update:1000] waybar-media";
|
||||||
|
color = "#eceff4";
|
||||||
|
font_size = "15";
|
||||||
|
font_family = "JetBrainsMono NFM";
|
||||||
|
position = "100, 100";
|
||||||
|
halign = "left";
|
||||||
|
valign = "bottom";
|
||||||
|
}
|
||||||
];
|
];
|
||||||
# user box
|
# user box
|
||||||
shape = [
|
shape = [
|
||||||
@@ -267,6 +278,19 @@ in
|
|||||||
shadow_passes = 2;
|
shadow_passes = 2;
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
image = [
|
||||||
|
{
|
||||||
|
monitor = cfg.primaryDisplay;
|
||||||
|
# path = "/tmp/hyprlock-art";
|
||||||
|
reload_cmd = "waybar-media-art";
|
||||||
|
reload_time = 3;
|
||||||
|
size = 150;
|
||||||
|
rounding = 0;
|
||||||
|
position = "100, 150";
|
||||||
|
halign = "left";
|
||||||
|
valign = "bottom";
|
||||||
|
}
|
||||||
|
];
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -328,6 +328,7 @@ in
|
|||||||
./options.nix
|
./options.nix
|
||||||
./scripts/audio-control.nix
|
./scripts/audio-control.nix
|
||||||
./scripts/hass.nix
|
./scripts/hass.nix
|
||||||
|
./scripts/media.nix
|
||||||
./scripts/notifications.nix
|
./scripts/notifications.nix
|
||||||
./scripts/weather.nix
|
./scripts/weather.nix
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ let
|
|||||||
if [[ -n "$artist" && -n "$title" ]]; then
|
if [[ -n "$artist" && -n "$title" ]]; then
|
||||||
echo "♪ $artist - $title"
|
echo "♪ $artist - $title"
|
||||||
elif [[ -n "$title" ]]; then
|
elif [[ -n "$title" ]]; then
|
||||||
echo "♪ $title"
|
echo "♪ ''\${title//&/&}"
|
||||||
else
|
else
|
||||||
echo "♪ Music Playing"
|
echo "♪ Music Playing"
|
||||||
fi
|
fi
|
||||||
@@ -43,15 +43,9 @@ let
|
|||||||
art=$(playerctl metadata mpris:artUrl 2>/dev/null)
|
art=$(playerctl metadata mpris:artUrl 2>/dev/null)
|
||||||
|
|
||||||
if [[ -n "$art" ]]; then
|
if [[ -n "$art" ]]; then
|
||||||
echo "♪ $artist - $title"
|
echo ''\${art#file://}
|
||||||
else
|
|
||||||
echo "♪ Music Playing"
|
|
||||||
fi
|
fi
|
||||||
else
|
|
||||||
echo ""
|
|
||||||
fi
|
fi
|
||||||
else
|
|
||||||
echo ""
|
|
||||||
fi
|
fi
|
||||||
'';
|
'';
|
||||||
in
|
in
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ let
|
|||||||
import shutil
|
import shutil
|
||||||
from datetime import datetime, timedelta
|
from datetime import datetime, timedelta
|
||||||
import argparse
|
import argparse
|
||||||
|
import math
|
||||||
|
|
||||||
import requests
|
import requests
|
||||||
|
|
||||||
@@ -25,6 +26,8 @@ let
|
|||||||
parser.add_argument('--hyprlock', action='store_true')
|
parser.add_argument('--hyprlock', action='store_true')
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
# --- MAPPINGS ---
|
||||||
|
|
||||||
WWO_CODE = {
|
WWO_CODE = {
|
||||||
"113": "Sunny",
|
"113": "Sunny",
|
||||||
"116": "PartlyCloudy",
|
"116": "PartlyCloudy",
|
||||||
@@ -76,6 +79,38 @@ let
|
|||||||
"395": "HeavySnowShowers",
|
"395": "HeavySnowShowers",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Maps WMO codes (OpenMeteo) to WWO codes (wttr.in)
|
||||||
|
WMO_TO_WWO = {
|
||||||
|
0: "113", # Clear sky -> Sunny
|
||||||
|
1: "113", # Mainly clear -> Sunny
|
||||||
|
2: "116", # Partly cloudy
|
||||||
|
3: "122", # Overcast -> VeryCloudy
|
||||||
|
45: "143", # Fog
|
||||||
|
48: "248", # Depositing rime fog
|
||||||
|
51: "266", # Drizzle: Light
|
||||||
|
53: "266", # Drizzle: Moderate (mapped to LightRain)
|
||||||
|
55: "296", # Drizzle: Dense intensity (LightRain usually suits better than heavy)
|
||||||
|
56: "281", # Freezing Drizzle: Light
|
||||||
|
57: "284", # Freezing Drizzle: Dense
|
||||||
|
61: "296", # Rain: Slight
|
||||||
|
63: "302", # Rain: Moderate
|
||||||
|
65: "308", # Rain: Heavy
|
||||||
|
66: "311", # Freezing Rain: Light
|
||||||
|
67: "314", # Freezing Rain: Heavy
|
||||||
|
71: "326", # Snow fall: Slight
|
||||||
|
73: "332", # Snow fall: Moderate
|
||||||
|
75: "338", # Snow fall: Heavy
|
||||||
|
77: "350", # Snow grains
|
||||||
|
80: "353", # Rain showers: Slight
|
||||||
|
81: "356", # Rain showers: Moderate
|
||||||
|
82: "359", # Rain showers: Violent
|
||||||
|
85: "368", # Snow showers: Slight
|
||||||
|
86: "371", # Snow showers: Heavy
|
||||||
|
95: "386", # Thunderstorm: Slight or moderate
|
||||||
|
96: "389", # Thunderstorm with slight hail
|
||||||
|
99: "395", # Thunderstorm with heavy hail
|
||||||
|
}
|
||||||
|
|
||||||
WEATHER_SYMBOL = {
|
WEATHER_SYMBOL = {
|
||||||
"Unknown": "",
|
"Unknown": "",
|
||||||
"Cloudy": "",
|
"Cloudy": "",
|
||||||
@@ -115,10 +150,6 @@ let
|
|||||||
"SSE": "↘",
|
"SSE": "↘",
|
||||||
}
|
}
|
||||||
|
|
||||||
MOON_PHASES = (
|
|
||||||
"", "", "", "", "", "", "", ""
|
|
||||||
)
|
|
||||||
|
|
||||||
WEATHER_SYMBOL_WI_DAY = {
|
WEATHER_SYMBOL_WI_DAY = {
|
||||||
"Unknown": "",
|
"Unknown": "",
|
||||||
"Cloudy": "",
|
"Cloudy": "",
|
||||||
@@ -297,21 +328,15 @@ let
|
|||||||
data["text"] = ""
|
data["text"] = ""
|
||||||
|
|
||||||
def format_time(time):
|
def format_time(time):
|
||||||
"""get the time formatted"""
|
|
||||||
return datetime.strptime(format_24_time(time), "%H").strftime("%I %p")
|
return datetime.strptime(format_24_time(time), "%H").strftime("%I %p")
|
||||||
|
|
||||||
def format_24_time(time):
|
def format_24_time(time):
|
||||||
"""get the time formatted"""
|
|
||||||
return time.replace("00", "").zfill(2)
|
return time.replace("00", "").zfill(2)
|
||||||
|
|
||||||
|
|
||||||
def format_temp(temp):
|
def format_temp(temp):
|
||||||
"""get the temp formatted"""
|
return (str(temp) + "°").ljust(3)
|
||||||
return (temp + "°").ljust(3)
|
|
||||||
|
|
||||||
|
|
||||||
def format_chances(hour):
|
def format_chances(hour):
|
||||||
"""get the chances formatted"""
|
|
||||||
chances = {
|
chances = {
|
||||||
"chanceoffog": "Fog",
|
"chanceoffog": "Fog",
|
||||||
"chanceoffrost": "Frost",
|
"chanceoffrost": "Frost",
|
||||||
@@ -322,62 +347,67 @@ let
|
|||||||
"chanceofthunder": "Thunder",
|
"chanceofthunder": "Thunder",
|
||||||
"chanceofwindy": "Wind",
|
"chanceofwindy": "Wind",
|
||||||
}
|
}
|
||||||
|
|
||||||
conditions = []
|
conditions = []
|
||||||
for chance, event in chances.items():
|
for chance, event in chances.items():
|
||||||
if int(hour[chance]) > 0:
|
if int(hour.get(chance, 0)) > 0:
|
||||||
conditions.append(event + " " + hour[chance] + "%")
|
conditions.append(event + " " + str(hour[chance]) + "%")
|
||||||
return ", ".join(conditions)
|
return ", ".join(conditions)
|
||||||
|
|
||||||
|
def deg_to_compass(num):
|
||||||
|
val = int((num / 22.5) + 0.5)
|
||||||
|
arr = ["N", "NNE", "NE", "ENE", "E", "ESE", "SE", "SSE", "S", "SSW", "SW", "WSW", "W", "WNW", "NW", "NNW"]
|
||||||
|
return arr[(val % 16)]
|
||||||
|
|
||||||
def build_text(current_condition):
|
def build_text(current_condition):
|
||||||
"""build the text string"""
|
|
||||||
feels_like_f = current_condition["FeelsLikeF"]
|
feels_like_f = current_condition["FeelsLikeF"]
|
||||||
weather_code = current_condition["weatherCode"]
|
weather_code = current_condition["weatherCode"]
|
||||||
|
# Check if we have a mapped format; if not, fallback to Unknown
|
||||||
|
if weather_code not in WEATHER_CODES:
|
||||||
|
weather_code = "113" # Fallback to sunny/default to prevent crash
|
||||||
|
|
||||||
tempint = int(feels_like_f)
|
tempint = int(float(feels_like_f)) # float cast just in case
|
||||||
extrachar = ""
|
extrachar = ""
|
||||||
if 0 < tempint < 10:
|
if 0 < tempint < 10:
|
||||||
extrachar = "+"
|
extrachar = "+"
|
||||||
|
|
||||||
current_weather = f"{WEATHER_CODES[weather_code]} {extrachar} {feels_like_f}°F"
|
current_weather = f"{WEATHER_CODES[weather_code]} {extrachar} {int(feels_like_f)}°F"
|
||||||
|
|
||||||
return current_weather
|
return current_weather
|
||||||
|
|
||||||
def build_tooltip(current_condition, astronomy, moon_icon):
|
def build_tooltip(current_condition, astronomy, moon_icon):
|
||||||
"""build the tooltip text"""
|
|
||||||
weather_description = current_condition['weatherDesc'][0]['value']
|
weather_description = current_condition['weatherDesc'][0]['value']
|
||||||
feels_like_f = current_condition["FeelsLikeF"]
|
feels_like_f = current_condition["FeelsLikeF"]
|
||||||
temp_f = current_condition['temp_F']
|
temp_f = current_condition['temp_F']
|
||||||
humidity = current_condition['humidity']
|
humidity = current_condition['humidity']
|
||||||
wind_speed = current_condition['windspeedMiles']
|
wind_speed = current_condition['windspeedMiles']
|
||||||
wind_dir = current_condition['winddir16Point']
|
wind_dir = current_condition['winddir16Point']
|
||||||
moon_phase = astronomy['moon_phase']
|
moon_phase = astronomy.get('moon_phase', 'Unknown')
|
||||||
wego = WEATHER_CODES_WEGO[current_condition['weatherCode']]
|
|
||||||
|
weather_code = current_condition['weatherCode']
|
||||||
|
if weather_code not in WEATHER_CODES_WEGO:
|
||||||
|
weather_code = "113"
|
||||||
|
|
||||||
|
wego = WEATHER_CODES_WEGO[weather_code]
|
||||||
|
|
||||||
current = f"{wego[0]}{weather_description} {temp_f}°\n"
|
current = f"{wego[0]}{weather_description} {temp_f}°\n"
|
||||||
feels = f"{wego[1]}Feels like: {feels_like_f}°\n"
|
feels = f"{wego[1]}Feels like: {feels_like_f}°\n"
|
||||||
wind = f"{wego[2]}Wind: {wind_speed}mph {WIND_DIRECTION[wind_dir]}\n"
|
wind = f"{wego[2]}Wind: {wind_speed}mph {WIND_DIRECTION.get(wind_dir, ''\'')}\n" # Safe get for direction
|
||||||
humidityl = f"{wego[3]}Humidity: {humidity}%\n"
|
humidityl = f"{wego[3]}Humidity: {humidity}%\n"
|
||||||
moon = f"{wego[4]}Moon phase: {moon_phase} " + moon_icon + "\n"
|
moon = f"{wego[4]}Moon phase: {moon_phase} " + moon_icon + "\n"
|
||||||
|
|
||||||
tooltip = current + feels + wind + humidityl + moon
|
tooltip = current + feels + wind + humidityl + moon
|
||||||
|
|
||||||
return tooltip
|
return tooltip
|
||||||
|
|
||||||
def build_forecast(weather):
|
def build_forecast(weather):
|
||||||
"""build a 3 day forecast"""
|
|
||||||
tooltip = "\n"
|
tooltip = "\n"
|
||||||
|
|
||||||
for i, day in enumerate(weather):
|
for i, day in enumerate(weather):
|
||||||
# determine day
|
|
||||||
if i == 0:
|
if i == 0:
|
||||||
tooltip += "Today, "
|
tooltip += "Today, "
|
||||||
if i == 1:
|
if i == 1:
|
||||||
tooltip += "Tomorrow, "
|
tooltip += "Tomorrow, "
|
||||||
# format the date
|
|
||||||
date = datetime.strptime(day['date'], "%Y-%m-%d").strftime("%a %b %d %Y")
|
date = datetime.strptime(day['date'], "%Y-%m-%d").strftime("%a %b %d %Y")
|
||||||
tooltip += f"<b>{date}</b>\n"
|
tooltip += f"<b>{date}</b>\n"
|
||||||
# set the high and low
|
|
||||||
max_temp = day['maxtempF']
|
max_temp = day['maxtempF']
|
||||||
min_temp = day['mintempF']
|
min_temp = day['mintempF']
|
||||||
tooltip += f" {max_temp}°F {min_temp}°F"
|
tooltip += f" {max_temp}°F {min_temp}°F"
|
||||||
@@ -389,29 +419,34 @@ let
|
|||||||
tooltip += build_hourly_forecast(i, day['hourly'], sunrise, sunset)
|
tooltip += build_hourly_forecast(i, day['hourly'], sunrise, sunset)
|
||||||
return tooltip
|
return tooltip
|
||||||
|
|
||||||
|
|
||||||
def build_hourly_forecast(day_num, hourly, sunrise, sunset):
|
def build_hourly_forecast(day_num, hourly, sunrise, sunset):
|
||||||
"""build an hourly forecast"""
|
try:
|
||||||
sunrise_hour = datetime.strptime(sunrise, "%I:%M %p").hour
|
sunrise_hour = datetime.strptime(sunrise, "%I:%M %p").hour
|
||||||
sunset_hour = datetime.strptime(sunset, "%I:%M %p").hour
|
sunset_hour = datetime.strptime(sunset, "%I:%M %p").hour
|
||||||
|
except ValueError:
|
||||||
|
# Fallback if time format is different (OpenMeteo might send 24h)
|
||||||
|
sunrise_hour = int(sunrise.split(':')[0])
|
||||||
|
sunset_hour = int(sunset.split(':')[0])
|
||||||
|
|
||||||
current_hour = datetime.now().hour
|
current_hour = datetime.now().hour
|
||||||
tooltip = ""
|
tooltip = ""
|
||||||
|
|
||||||
for hour in hourly:
|
for hour in hourly:
|
||||||
time_24_hr = int(format_24_time(hour["time"]))
|
time_24_hr = int(format_24_time(hour["time"]))
|
||||||
|
|
||||||
if day_num == 0:
|
if day_num == 0:
|
||||||
if time_24_hr < current_hour - 2:
|
if time_24_hr < current_hour - 2:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
# determine which code to use
|
|
||||||
if is_night_hour(time_24_hr, sunrise_hour, sunset_hour):
|
if is_night_hour(time_24_hr, sunrise_hour, sunset_hour):
|
||||||
codes = WEATHER_CODES_WI_NIGHT
|
codes = WEATHER_CODES_WI_NIGHT
|
||||||
else:
|
else:
|
||||||
codes = WEATHER_CODES_WI_DAY
|
codes = WEATHER_CODES_WI_DAY
|
||||||
|
|
||||||
current_time = format_time(hour['time'])
|
current_time = format_time(hour['time'])
|
||||||
current_weather_code = codes[hour['weatherCode']]
|
wcode = hour['weatherCode']
|
||||||
|
if wcode not in codes: wcode = "113" # Fallback
|
||||||
|
|
||||||
|
current_weather_code = codes[wcode]
|
||||||
feels_like = format_temp(hour['FeelsLikeF'])
|
feels_like = format_temp(hour['FeelsLikeF'])
|
||||||
weather_desc = hour['weatherDesc'][0]['value']
|
weather_desc = hour['weatherDesc'][0]['value']
|
||||||
current_chances = format_chances(hour)
|
current_chances = format_chances(hour)
|
||||||
@@ -422,69 +457,160 @@ let
|
|||||||
return tooltip
|
return tooltip
|
||||||
|
|
||||||
def is_night_hour(time_24_hr, sunrise_hour, sunset_hour):
|
def is_night_hour(time_24_hr, sunrise_hour, sunset_hour):
|
||||||
"""returns true if the hour is night"""
|
|
||||||
before_sunrise = time_24_hr < sunrise_hour
|
before_sunrise = time_24_hr < sunrise_hour
|
||||||
after_sunset = time_24_hr > sunset_hour
|
after_sunset = time_24_hr > sunset_hour
|
||||||
return after_sunset or before_sunrise
|
return after_sunset or before_sunrise
|
||||||
|
|
||||||
def load_cache(path, ttl):
|
def load_cache(path, ttl):
|
||||||
"""Load cached file if it is not too old."""
|
|
||||||
try:
|
try:
|
||||||
if not os.path.exists(path):
|
if not os.path.exists(path):
|
||||||
return None
|
return None
|
||||||
|
|
||||||
mtime = datetime.fromtimestamp(os.path.getmtime(path))
|
mtime = datetime.fromtimestamp(os.path.getmtime(path))
|
||||||
if datetime.now() - mtime > ttl:
|
if datetime.now() - mtime > ttl:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
with open(path, "r") as f:
|
with open(path, "r") as f:
|
||||||
if path.endswith(".json"):
|
if path.endswith(".json"):
|
||||||
return json.load(f)
|
return json.load(f)
|
||||||
return f.read().strip()
|
return f.read().strip()
|
||||||
|
|
||||||
except Exception:
|
except Exception:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
def save_cache(path, data):
|
def save_cache(path, data):
|
||||||
"""Write cache file safely."""
|
|
||||||
os.makedirs(os.path.dirname(path), exist_ok=True)
|
os.makedirs(os.path.dirname(path), exist_ok=True)
|
||||||
tmp = path + ".tmp"
|
tmp = path + ".tmp"
|
||||||
|
|
||||||
try:
|
try:
|
||||||
with open(tmp, "w") as f:
|
with open(tmp, "w") as f:
|
||||||
if isinstance(data, dict):
|
if isinstance(data, dict):
|
||||||
json.dump(data, f)
|
json.dump(data, f)
|
||||||
else:
|
else:
|
||||||
f.write(str(data))
|
f.write(str(data))
|
||||||
|
|
||||||
shutil.move(tmp, path)
|
shutil.move(tmp, path)
|
||||||
except Exception:
|
except Exception:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
# --- OPEN-METEO INTEGRATION HELPER FUNCTIONS ---
|
||||||
|
|
||||||
|
def get_lat_lon():
|
||||||
|
"""Attempt to get location via IP if using OpenMeteo"""
|
||||||
|
try:
|
||||||
|
resp = requests.get("http://ip-api.com/json/", timeout=5).json()
|
||||||
|
return resp.get('lat'), resp.get('lon')
|
||||||
|
except:
|
||||||
|
# Default to a generic location if IP fetch fails (NYC)
|
||||||
|
return 40.71, -74.00
|
||||||
|
|
||||||
|
def fetch_open_meteo():
|
||||||
|
"""Fetch and Transform OpenMeteo data to match Wttr.in JSON structure"""
|
||||||
|
lat, lon = get_lat_lon()
|
||||||
|
url = "https://api.open-meteo.com/v1/forecast"
|
||||||
|
params = {
|
||||||
|
"latitude": lat,
|
||||||
|
"longitude": lon,
|
||||||
|
"current": "temperature_2m,apparent_temperature,precipitation,weather_code,wind_speed_10m,wind_direction_10m,relative_humidity_2m",
|
||||||
|
"daily": "weather_code,temperature_2m_max,temperature_2m_min,sunrise,sunset,precipitation_probability_max",
|
||||||
|
"hourly": "temperature_2m,apparent_temperature,precipitation_probability,weather_code",
|
||||||
|
"temperature_unit": "fahrenheit",
|
||||||
|
"wind_speed_unit": "mph",
|
||||||
|
"precipitation_unit": "inch",
|
||||||
|
"timezone": "auto"
|
||||||
|
}
|
||||||
|
|
||||||
|
response = requests.get(url, params=params, timeout=10)
|
||||||
|
om_data = response.json()
|
||||||
|
|
||||||
|
# Transform Current Condition
|
||||||
|
current = om_data["current"]
|
||||||
|
wmo_code = current["weather_code"]
|
||||||
|
wwo_code = WMO_TO_WWO.get(wmo_code, "113")
|
||||||
|
|
||||||
|
wttr_current = {
|
||||||
|
"temp_F": str(int(current["temperature_2m"])),
|
||||||
|
"FeelsLikeF": str(int(current["apparent_temperature"])),
|
||||||
|
"weatherCode": wwo_code,
|
||||||
|
"weatherDesc": [{"value": WWO_CODE.get(wwo_code, "Unknown")}],
|
||||||
|
"humidity": str(current["relative_humidity_2m"]),
|
||||||
|
"windspeedMiles": str(int(current["wind_speed_10m"])),
|
||||||
|
"winddir16Point": deg_to_compass(current["wind_direction_10m"]),
|
||||||
|
}
|
||||||
|
|
||||||
|
# Transform Daily Forecast (OpenMeteo gives 7 days, we need 3)
|
||||||
|
wttr_weather = []
|
||||||
|
daily = om_data["daily"]
|
||||||
|
hourly = om_data["hourly"]
|
||||||
|
|
||||||
|
for i in range(3):
|
||||||
|
date_str = daily["time"][i]
|
||||||
|
|
||||||
|
# Build Hourly for this day (wttr uses 3-hour intervals: 0, 300, 600...)
|
||||||
|
# OpenMeteo gives 0, 1, 2...
|
||||||
|
wttr_hourly = []
|
||||||
|
for h in range(0, 24, 3): # Step by 3 hours to mimic wttr
|
||||||
|
idx = (i * 24) + h
|
||||||
|
h_code = hourly["weather_code"][idx]
|
||||||
|
h_wwo = WMO_TO_WWO.get(h_code, "113")
|
||||||
|
|
||||||
|
wttr_hourly.append({
|
||||||
|
"time": str(h * 100), # 0, 300, 600
|
||||||
|
"weatherCode": h_wwo,
|
||||||
|
"weatherDesc": [{"value": WWO_CODE.get(h_wwo, "Unknown")}],
|
||||||
|
"FeelsLikeF": str(int(hourly["apparent_temperature"][idx])),
|
||||||
|
"chanceofrain": str(hourly["precipitation_probability"][idx]),
|
||||||
|
# Fill other chances with 0 as API doesn't provide them easily
|
||||||
|
"chanceoffog": "0", "chanceofsnow": "0", "chanceofthunder": "0",
|
||||||
|
"chanceofwindy": "0", "chanceofsunshine": "0"
|
||||||
|
})
|
||||||
|
|
||||||
|
# Astronomy
|
||||||
|
sunrise = datetime.fromisoformat(daily["sunrise"][i]).strftime("%I:%M %p")
|
||||||
|
sunset = datetime.fromisoformat(daily["sunset"][i]).strftime("%I:%M %p")
|
||||||
|
|
||||||
|
wttr_weather.append({
|
||||||
|
"date": date_str,
|
||||||
|
"maxtempF": str(int(daily["temperature_2m_max"][i])),
|
||||||
|
"mintempF": str(int(daily["temperature_2m_min"][i])),
|
||||||
|
"astronomy": [{"sunrise": sunrise, "sunset": sunset, "moon_phase": "Unknown"}],
|
||||||
|
"hourly": wttr_hourly
|
||||||
|
})
|
||||||
|
|
||||||
|
return {
|
||||||
|
"current_condition": [wttr_current],
|
||||||
|
"weather": wttr_weather
|
||||||
|
}
|
||||||
|
|
||||||
def get_wttr_json(hyprlock=False):
|
def get_wttr_json(hyprlock=False):
|
||||||
"""get the weather json"""
|
|
||||||
|
|
||||||
# Try loading cached JSON
|
# Try loading cached JSON
|
||||||
cached = load_cache(CACHE_FILE, CACHE_TTL)
|
cached = load_cache(CACHE_FILE, CACHE_TTL)
|
||||||
cached_moon = load_cache(CACHE_MOON_FILE, CACHE_TTL)
|
cached_moon = load_cache(CACHE_MOON_FILE, CACHE_TTL)
|
||||||
cached_moon_icon = load_cache(CACHE_MOON_ICON_FILE, CACHE_TTL)
|
cached_moon_icon = load_cache(CACHE_MOON_ICON_FILE, CACHE_TTL)
|
||||||
|
|
||||||
if cached and cached_moon and cached_moon_icon:
|
if cached and cached_moon and cached_moon_icon:
|
||||||
current_condition = cached
|
weather = cached
|
||||||
|
current_condition = weather["current_condition"][0]
|
||||||
astronomy = cached_moon
|
astronomy = cached_moon
|
||||||
moon_icon = cached_moon_icon
|
moon_icon = cached_moon_icon
|
||||||
else:
|
else:
|
||||||
weather = requests.get("https://wttr.in/?u&format=j1", timeout=30).json()
|
try:
|
||||||
moon = requests.get("https://wttr.in/?format=%m", timeout=30)
|
# Primary Source: wttr.in
|
||||||
moon_icon = moon.text
|
weather = requests.get("https://wttr.in/?u&format=j1", timeout=5).json()
|
||||||
|
moon = requests.get("https://wttr.in/?format=%m", timeout=5)
|
||||||
|
moon_icon = moon.text
|
||||||
|
|
||||||
|
current_condition = weather["current_condition"][0]
|
||||||
|
astronomy = weather["weather"][0]['astronomy'][0]
|
||||||
|
except Exception:
|
||||||
|
# Fallback Source: Open-Meteo
|
||||||
|
try:
|
||||||
|
print("open_mateo fallback")
|
||||||
|
weather = fetch_open_meteo()
|
||||||
|
current_condition = weather["current_condition"][0]
|
||||||
|
astronomy = weather["weather"][0]['astronomy'][0]
|
||||||
|
moon_icon = "" # Generic moon icon for fallback
|
||||||
|
except Exception as e:
|
||||||
|
# If both fail
|
||||||
|
raise e
|
||||||
|
|
||||||
current_condition = weather["current_condition"][0]
|
# Save cache (works for both sources since we transformed OM data)
|
||||||
astronomy = weather["weather"][0]['astronomy'][0]
|
save_cache(CACHE_FILE, weather)
|
||||||
|
|
||||||
# Save cache
|
|
||||||
save_cache(CACHE_FILE, current_condition)
|
|
||||||
save_cache(CACHE_MOON_FILE, astronomy)
|
save_cache(CACHE_MOON_FILE, astronomy)
|
||||||
save_cache(CACHE_MOON_ICON_FILE, moon_icon)
|
save_cache(CACHE_MOON_ICON_FILE, moon_icon)
|
||||||
|
|
||||||
@@ -492,30 +618,27 @@ let
|
|||||||
return build_tooltip(current_condition, astronomy, moon_icon)
|
return build_tooltip(current_condition, astronomy, moon_icon)
|
||||||
else:
|
else:
|
||||||
text = build_text(current_condition)
|
text = build_text(current_condition)
|
||||||
|
|
||||||
tooltip = build_tooltip(current_condition, astronomy, moon_icon) + build_forecast(weather["weather"])
|
tooltip = build_tooltip(current_condition, astronomy, moon_icon) + build_forecast(weather["weather"])
|
||||||
|
|
||||||
data["text"] = text
|
data["text"] = text
|
||||||
data["tooltip"] = tooltip
|
data["tooltip"] = tooltip
|
||||||
|
|
||||||
return json.dumps(data)
|
return json.dumps(data)
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
"""main"""
|
|
||||||
if args.hyprlock:
|
if args.hyprlock:
|
||||||
try:
|
try:
|
||||||
print(get_wttr_json(hyprlock=True))
|
print(get_wttr_json(hyprlock=True))
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print("error")
|
print("error")
|
||||||
print(e)
|
# print(e) # Uncomment for debug
|
||||||
else:
|
else:
|
||||||
try:
|
try:
|
||||||
print(get_wttr_json())
|
print(get_wttr_json())
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print("error")
|
print(json.dumps({"text": "Err", "tooltip": str(e)}))
|
||||||
print(e)
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
|
|
||||||
main()
|
|
||||||
'';
|
'';
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
|
|||||||
Reference in New Issue
Block a user