#!/usr/bin/env python # vim: syntax=python import os import json import requests import logging from datetime import datetime, timedelta logging.basicConfig(level=logging.ERROR) logger = logging.getLogger(__name__) CACHE_EXPIRATION = 60 XDG_CACHE_HOME = os.getenv("XDG_CACHE_HOME", os.path.expanduser("~/.cache")) CACHE_DIR = os.path.join(XDG_CACHE_HOME, "zephyr") FALLBACK_CACHE_DIR = "/tmp" CACHE_FILE = os.path.join(CACHE_DIR, "zephyr_cache.json") SUNNY = "\udb81\udda8" CLOUDY = "\ue312" RAIN = "\ue318" SNOW = "\ue31a" THUNDERSTORM = "\ue31d" PARTLY_CLOUDY = "\ue302" CLEAR = "\ue30d" HOURS_AGO_THRESHOLD = 2 TEMP_THRESHOLD_COLD = 10 TEMP_THRESHOLD_HOT = 0 def ensure_cache_directory(): try: if not os.path.exists(CACHE_DIR): os.makedirs(CACHE_DIR, exist_ok=True) except Exception as e: logger.error(f"Error creating cache directory: {e}") def get_weather_data(): ensure_cache_directory() try: response = requests.get("https://wttr.in/?format=j1") response.raise_for_status() return response.json() except requests.exceptions.RequestException as e: logger.error(f"Error fetching weather data: {e}") return None def get_cached_weather_data(): try: if os.path.exists(CACHE_FILE): with open(CACHE_FILE, "r") as cache_file: cached_data = json.load(cache_file) cache_time = datetime.strptime( cached_data["timestamp"], "%Y-%m-%d %H:%M:%S" ) if datetime.now() - cache_time < timedelta(minutes=CACHE_EXPIRATION): return cached_data["data"] except Exception as e: logger.error(f"Error loading cached data: {e}") return None def cache_weather_data(data): try: with open(CACHE_FILE, "w") as cache_file: cached_data = { "data": data, "timestamp": datetime.now().strftime("%Y-%m-%d %H:%M:%S"), } json.dump(cached_data, cache_file) except Exception as e: logger.error(f"Error caching data: {e}") def format_time(time): return time.replace("00", "").zfill(2) def format_temp(temp): return f" {temp}°".ljust(4) def get_emoji_for_condition(condition): emoji_map = { "Sunny": SUNNY, "Partly cloudy": PARTLY_CLOUDY, "Overcast": CLOUDY, "Patchy rain nearby": RAIN, "Clear": CLEAR, "Fog": "\ue313", "Frost": "\udb83\udf29", "Thunder": THUNDERSTORM, "Snow": SNOW, "Windy": "\u27A7", "Mist": "\u2601", "Drizzle": "\u2601", "Heavy rain": "\u2614", "Sleet": "\u2744", "Wintry mix": "\u2744", "Clear/Sunny": CLEAR, "Clear/Mostly clear": CLEAR, "Clear/Mostly clear (night)": CLEAR, "Drizzle (night)": "\u2601", } return emoji_map.get(condition, "") def format_conditions(hour): condition_probabilities = { "chanceoffog": "Fog", "chanceoffrost": "Frost", "chanceofovercast": "Overcast", "chanceofrain": "Rain", "chanceofsnow": "Snow", "chanceofsunshine": "Sunshine", "chanceofthunder": "Thunder", "chanceofwindy": "Wind", } if "chanceofpartlycloudy" in hour: condition_probabilities["chanceofpartlycloudy"] = "Partly Cloudy" conditions = [] for event, description in condition_probabilities.items(): if event in hour: probability = int(hour[event]) if probability > 0: emoji = get_emoji_for_condition(description) conditions.append(f"{emoji} {description} {probability}%") return ", ".join(conditions) def format_weather_data(weather_data): current_condition = weather_data["current_condition"][0] temp = int(current_condition["FeelsLikeC"]) temp_sign = "+" if TEMP_THRESHOLD_HOT > temp > TEMP_THRESHOLD_COLD else "" formatted_data = { "text": f" {SUNNY} \n {temp_sign}{temp}°", "tooltip": f"{current_condition['weatherDesc'][0]['value']} {current_condition['temp_C']}°\n" f"Feels like: {current_condition['FeelsLikeC']}°\n" f"Wind: {current_condition['windspeedKmph']}Km/h\n" f"Humidity: {current_condition['humidity']}%\n", } for i, day in enumerate(weather_data["weather"]): formatted_data["tooltip"] += f"\n" if i == 0: formatted_data["tooltip"] += "Today, " if i == 1: formatted_data["tooltip"] += "Tomorrow, " formatted_data["tooltip"] += f"{day['date']}\n" formatted_data["tooltip"] += f"⬆️ {day['maxtempC']}° ⬇️ {day['mintempC']}° " formatted_data[ "tooltip" ] += f"🌅 {day['astronomy'][0]['sunrise']} 🌇 {day['astronomy'][0]['sunset']}\n" now = datetime.now() for hour in day["hourly"]: hour_time = format_time(hour["time"]) if i == 0 and int(hour_time) < now.hour - HOURS_AGO_THRESHOLD: continue formatted_data[ "tooltip" ] += f"{hour_time} {get_emoji_for_condition(hour['weatherDesc'][0]['value'])} {format_temp(hour['FeelsLikeC'])} {hour['weatherDesc'][0]['value']}, {format_conditions(hour)}\n" return formatted_data def main(): weather_data = get_weather_data() if weather_data is None: weather_data = get_cached_weather_data() if weather_data: formatted_data = format_weather_data(weather_data) cache_weather_data(formatted_data) print(json.dumps(formatted_data)) if __name__ == "__main__": main()