#!/usr/bin/env python3 """Weather collector using wttr.in.""" import json import subprocess import urllib.request from pathlib import Path def fetch_weather(location: str) -> dict: """Fetch weather data from wttr.in.""" # Use wttr.in JSON format url = f"https://wttr.in/{location}?format=j1" req = urllib.request.Request(url, headers={"User-Agent": "Mozilla/5.0"}) try: with urllib.request.urlopen(req, timeout=5) as resp: return json.load(resp) except Exception as e: return {"error": str(e)} def format_weather_basic(data: dict, location: str) -> str: """Format weather data without LLM - basic fallback.""" if "error" in data: return f"Weather unavailable: {data['error']}" try: current = data["current_condition"][0] today = data["weather"][0] temp_f = current.get("temp_F", "?") desc = current.get("weatherDesc", [{}])[0].get("value", "Unknown") high = today.get("maxtempF", "?") low = today.get("mintempF", "?") return f"{location}: {temp_f}°F, {desc} | High {high}° Low {low}°" except Exception as e: return f"Weather parse error: {e}" def format_weather_with_haiku(data: dict, location: str) -> str: """Use Haiku to format weather data nicely.""" if "error" in data: return f"Weather unavailable: {data['error']}" try: current = data["current_condition"][0] today = data["weather"][0] # Extract key data temp_f = current.get("temp_F", "?") feels_like = current.get("FeelsLikeF", temp_f) desc = current.get("weatherDesc", [{}])[0].get("value", "Unknown") humidity = current.get("humidity", "?") high = today.get("maxtempF", "?") low = today.get("mintempF", "?") # Check for precipitation hourly = today.get("hourly", []) rain_hours = [h for h in hourly if int(h.get("chanceofrain", 0)) > 50] # Build context for Haiku weather_context = f"""Current: {temp_f}°F (feels like {feels_like}°F), {desc} High: {high}°F, Low: {low}°F Humidity: {humidity}% Rain chance >50%: {len(rain_hours)} hours today""" # Check if claude is available claude_path = Path.home() / ".local/bin/claude" if not claude_path.exists(): claude_path = "claude" # Try PATH prompt = f"""Format this weather data for {location} into a single concise line for a morning report. Add a brief hint if relevant (e.g., "bring umbrella", "nice day for a walk"). Keep it under 80 characters if possible. {weather_context} Output ONLY the formatted weather line, nothing else.""" result = subprocess.run( [str(claude_path), "--print", "--model", "haiku", "-p", prompt], capture_output=True, text=True, timeout=30 ) if result.returncode == 0 and result.stdout.strip(): return result.stdout.strip() else: # Fallback to basic format return format_weather_basic(data, location) except Exception as e: # Fallback to basic format return format_weather_basic(data, location) def collect(config: dict) -> dict: """Main collector entry point.""" location = config.get("weather", {}).get("location", "Seattle,WA,USA") city_name = location.split(",")[0] data = fetch_weather(location) # Try Haiku formatting, fall back to basic formatted = format_weather_with_haiku(data, city_name) return { "section": "Weather", "icon": "🌤", "content": formatted, "raw": data if "error" not in data else None, "error": data.get("error") } if __name__ == "__main__": # Test config = {"weather": {"location": "Seattle,WA,USA"}} result = collect(config) print(f"## {result['icon']} {result['section']}") print(result["content"])