Per-person briefings (Eric + Angela) with configurable weather locations
This commit is contained in:
+69
-25
@@ -1,8 +1,8 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Weather Agent
|
||||
Fetches weather for Providence, UT and returns structured data + markdown section.
|
||||
Called by the Daily Briefing agent. Can also run standalone.
|
||||
Fetches weather for a configurable location and returns structured data + markdown.
|
||||
Called by Daily Briefing agents with location config. Can also run standalone.
|
||||
"""
|
||||
|
||||
import sys
|
||||
@@ -11,18 +11,14 @@ from shared import MT, api_request, log_run
|
||||
|
||||
AGENT_ID = "weather"
|
||||
|
||||
# Providence, UT
|
||||
LAT = 41.7064
|
||||
LON = -111.8133
|
||||
|
||||
WEATHER_URL = (
|
||||
f"https://api.open-meteo.com/v1/forecast?"
|
||||
f"latitude={LAT}&longitude={LON}"
|
||||
f"¤t=temperature_2m,apparent_temperature,weather_code,wind_speed_10m,relative_humidity_2m"
|
||||
f"&daily=weather_code,temperature_2m_max,temperature_2m_min,precipitation_sum,wind_speed_10m_max,sunrise,sunset"
|
||||
f"&temperature_unit=fahrenheit&wind_speed_unit=mph&precipitation_unit=inch"
|
||||
f"&timezone=America/Denver&forecast_days=7"
|
||||
)
|
||||
# Default location (Providence, UT)
|
||||
DEFAULT_LOCATION = {
|
||||
"name": "Providence",
|
||||
"state": "Utah",
|
||||
"country": "US",
|
||||
"lat": 41.7064,
|
||||
"lon": -111.8133,
|
||||
}
|
||||
|
||||
WMO_CODES = {
|
||||
0: "Clear sky", 1: "Mainly clear", 2: "Partly cloudy", 3: "Overcast",
|
||||
@@ -38,13 +34,41 @@ WMO_CODES = {
|
||||
DAY_NAMES = ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"]
|
||||
|
||||
|
||||
def fetch_weather():
|
||||
"""Fetch weather data from Open-Meteo."""
|
||||
return api_request(WEATHER_URL)
|
||||
def build_url(location):
|
||||
"""Build the Open-Meteo API URL for a given location."""
|
||||
lat = location["lat"]
|
||||
lon = location["lon"]
|
||||
return (
|
||||
f"https://api.open-meteo.com/v1/forecast?"
|
||||
f"latitude={lat}&longitude={lon}"
|
||||
f"¤t=temperature_2m,apparent_temperature,weather_code,wind_speed_10m,relative_humidity_2m"
|
||||
f"&daily=weather_code,temperature_2m_max,temperature_2m_min,precipitation_sum,wind_speed_10m_max,sunrise,sunset"
|
||||
f"&temperature_unit=fahrenheit&wind_speed_unit=mph&precipitation_unit=inch"
|
||||
f"&timezone=America/Denver&forecast_days=7"
|
||||
)
|
||||
|
||||
|
||||
def format_section(weather):
|
||||
def location_label(location):
|
||||
"""Human-readable location string."""
|
||||
parts = [location.get("name", "")]
|
||||
if location.get("state"):
|
||||
parts.append(location["state"])
|
||||
if location.get("country") and location["country"] != "US":
|
||||
parts.append(location["country"])
|
||||
return ", ".join(p for p in parts if p)
|
||||
|
||||
|
||||
def fetch_weather(location=None):
|
||||
"""Fetch weather data from Open-Meteo for a given location."""
|
||||
loc = location or DEFAULT_LOCATION
|
||||
url = build_url(loc)
|
||||
return api_request(url)
|
||||
|
||||
|
||||
def format_section(weather, location=None):
|
||||
"""Format weather into a markdown section and a one-line summary."""
|
||||
loc = location or DEFAULT_LOCATION
|
||||
label = location_label(loc)
|
||||
now = datetime.now(MT)
|
||||
current = weather["current"]
|
||||
daily = weather["daily"]
|
||||
@@ -55,7 +79,7 @@ def format_section(weather):
|
||||
wind = round(current["wind_speed_10m"])
|
||||
humidity = current["relative_humidity_2m"]
|
||||
|
||||
md = "## Weather\n\n"
|
||||
md = f"## Weather — {label}\n\n"
|
||||
md += "### Current Conditions\n\n"
|
||||
md += "| | |\n|---|---|\n"
|
||||
md += f"| **Condition** | {condition} |\n"
|
||||
@@ -88,21 +112,41 @@ def format_section(weather):
|
||||
sunset = daily["sunset"][0].split("T")[1] if daily["sunset"][0] else "?"
|
||||
md += f"\n**Sunrise:** {sunrise} | **Sunset:** {sunset}\n"
|
||||
|
||||
summary = f"{condition}, {temp}°F (feels like {feels}°F), wind {wind} mph"
|
||||
summary = f"{label}: {condition}, {temp}°F (feels like {feels}°F), wind {wind} mph"
|
||||
return md, summary
|
||||
|
||||
|
||||
def run():
|
||||
def run(location=None):
|
||||
"""Run the weather agent. Returns (markdown_section, summary) or raises."""
|
||||
weather = fetch_weather()
|
||||
section, summary = format_section(weather)
|
||||
log_run(AGENT_ID, "success", output=summary)
|
||||
loc = location or DEFAULT_LOCATION
|
||||
weather = fetch_weather(loc)
|
||||
section, summary = format_section(weather, loc)
|
||||
log_run(AGENT_ID, "success", output=summary, metadata={"location": location_label(loc)})
|
||||
return section, summary
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
import argparse
|
||||
parser = argparse.ArgumentParser(description="Weather Agent")
|
||||
parser.add_argument("--lat", type=float, help="Latitude")
|
||||
parser.add_argument("--lon", type=float, help="Longitude")
|
||||
parser.add_argument("--name", type=str, help="City/location name")
|
||||
parser.add_argument("--state", type=str, help="State")
|
||||
parser.add_argument("--country", type=str, default="US", help="Country code")
|
||||
args = parser.parse_args()
|
||||
|
||||
loc = DEFAULT_LOCATION
|
||||
if args.lat and args.lon:
|
||||
loc = {
|
||||
"name": args.name or "Custom",
|
||||
"state": args.state or "",
|
||||
"country": args.country,
|
||||
"lat": args.lat,
|
||||
"lon": args.lon,
|
||||
}
|
||||
|
||||
try:
|
||||
section, summary = run()
|
||||
section, summary = run(loc)
|
||||
print(section)
|
||||
print(f"\nSummary: {summary}")
|
||||
except Exception as e:
|
||||
|
||||
Reference in New Issue
Block a user