|
12 | 12 | # export OPENWEATHERMAP_API_KEY="<your API key>" |
13 | 13 | # export MAPTILER_API_KEY="<your API key>" |
14 | 14 |
|
| 15 | +# Import the NWS data fetching function |
| 16 | +from api_get_nws_weather import get_nws_forecast, get_nws_alerts |
| 17 | + |
15 | 18 | # date & time utils |
16 | 19 | import datetime as dt |
17 | 20 | from dateutil import parser |
@@ -74,8 +77,38 @@ async def get_weather(city_name, country, exclude='', units='metric', lang='fi') |
74 | 77 | additional_data = await get_additional_data_dump() |
75 | 78 | logging.info(f"Additional data fetched: {additional_data}") |
76 | 79 |
|
77 | | - combined_data = await combine_weather_data(city_name, resolved_country, lat, lon, current_weather_data, forecast_data, moon_phase_data, daily_forecast_data, current_weather_data_from_weatherapi, astronomy_data, additional_data) |
78 | | - return combined_data |
| 80 | + # // (old method) |
| 81 | + # combined_data = await combine_weather_data(city_name, resolved_country, lat, lon, current_weather_data, forecast_data, moon_phase_data, daily_forecast_data, current_weather_data_from_weatherapi, astronomy_data, additional_data) |
| 82 | + # return combined_data |
| 83 | + |
| 84 | + # Fetch NWS data |
| 85 | + logging.info("Fetching NWS data.") |
| 86 | + nws_data = await get_nws_forecast(lat, lon) |
| 87 | + if nws_data: |
| 88 | + logging.info("NWS data fetched successfully.") |
| 89 | + nws_forecast = nws_data.get('nws_forecast') |
| 90 | + nws_forecast_hourly = nws_data.get('nws_forecast_hourly') |
| 91 | + else: |
| 92 | + logging.warning("Failed to fetch NWS data.") |
| 93 | + nws_forecast = None |
| 94 | + nws_forecast_hourly = None |
| 95 | + |
| 96 | + # Fetch NWS alerts data |
| 97 | + logging.info("Fetching NWS alerts data.") |
| 98 | + nws_alerts = await get_nws_alerts(lat, lon) |
| 99 | + if nws_alerts: |
| 100 | + logging.info(f"Fetched {len(nws_alerts)} active NWS alerts.") |
| 101 | + else: |
| 102 | + logging.info("No active NWS alerts found.") |
| 103 | + |
| 104 | + combined_data = await combine_weather_data( |
| 105 | + city_name, resolved_country, lat, lon, |
| 106 | + current_weather_data, forecast_data, moon_phase_data, |
| 107 | + daily_forecast_data, current_weather_data_from_weatherapi, |
| 108 | + astronomy_data, additional_data, nws_forecast, nws_forecast_hourly |
| 109 | + ) |
| 110 | + return combined_data |
| 111 | + |
79 | 112 | else: |
80 | 113 | logging.error(f"Failed to fetch weather data: {current_weather_response.text} / {forecast_response.text}") |
81 | 114 | return "[Inform the user that data fetching the weather data failed, current information could not be fetched. Reply in the user's language.]" |
@@ -172,7 +205,9 @@ def convert_to_24_hour(time_str, timezone_str): |
172 | 205 | return "Invalid time" |
173 | 206 |
|
174 | 207 | # combined weather data |
175 | | -async def combine_weather_data(city_name, country, lat, lon, current_weather_data, forecast_data, moon_phase_data, daily_forecast_data, current_weather_data_from_weatherapi, astronomy_data, additional_data): |
| 208 | +# async def combine_weather_data(city_name, country, lat, lon, current_weather_data, forecast_data, moon_phase_data, daily_forecast_data, current_weather_data_from_weatherapi, astronomy_data, additional_data): |
| 209 | +# Define the combine_weather_data function with NWS integration |
| 210 | +async def combine_weather_data(city_name, country, lat, lon, current_weather_data, forecast_data, moon_phase_data, daily_forecast_data, current_weather_data_from_weatherapi, astronomy_data, additional_data, nws_forecast, nws_forecast_hourly): |
176 | 211 | tf = TimezoneFinder() |
177 | 212 | timezone_str = tf.timezone_at(lat=lat, lng=lon) |
178 | 213 | local_timezone = pytz.timezone(timezone_str) |
@@ -285,7 +320,7 @@ async def combine_weather_data(city_name, country, lat, lon, current_weather_dat |
285 | 320 | "\n".join( |
286 | 321 | [f"Alert: {alert['headline']}\nDescription: {alert['desc']}\nInstructions: {alert['instruction']}\n" |
287 | 322 | for alert in alerts['alert']] |
288 | | - ) if 'alert' in alerts and alerts['alert'] else "No weather alerts." |
| 323 | + ) if 'alert' in alerts and alerts['alert'] else "No weather alerts according to OpenWeatherMap. NOTE: Please see other sources (i.e. NWS) to be sure." |
289 | 324 | ) |
290 | 325 |
|
291 | 326 | detailed_weather_info += f"\n{air_quality_info}\n{alerts_info}" |
@@ -324,6 +359,113 @@ async def combine_weather_data(city_name, country, lat, lon, current_weather_dat |
324 | 359 |
|
325 | 360 | combined_info = f"{detailed_weather_info}\n\n{final_forecast}" |
326 | 361 |
|
| 362 | + |
| 363 | + # Append NWS data (Forecasts) |
| 364 | + if nws_forecast: |
| 365 | + nws_forecast_info = "" |
| 366 | + nws_periods = nws_forecast.get('properties', {}).get('periods', []) |
| 367 | + if nws_periods: |
| 368 | + nws_forecast_info += "🌦️ <b>NWS Forecast (weather.gov):</b>\n" |
| 369 | + for period in nws_periods[:3]: # Limit to next 3 periods |
| 370 | + name = period.get('name', 'N/A') |
| 371 | + temperature = period.get('temperature', 'N/A') |
| 372 | + temperature_unit = period.get('temperatureUnit', 'N/A') |
| 373 | + wind_speed = period.get('windSpeed', 'N/A') |
| 374 | + wind_direction = period.get('windDirection', 'N/A') |
| 375 | + short_forecast = period.get('shortForecast', 'N/A') |
| 376 | + nws_forecast_info += f"{name}: {short_forecast}, {temperature}°{temperature_unit}, Wind: {wind_speed} {wind_direction}\n" |
| 377 | + else: |
| 378 | + nws_forecast_info += "🌦️ <b>NWS Forecast (weather.gov):</b> Ei saatavilla.\n" |
| 379 | + |
| 380 | + if nws_forecast_hourly: |
| 381 | + nws_hourly_forecast_info = "" |
| 382 | + nws_hourly_periods = nws_forecast_hourly.get('properties', {}).get('periods', []) |
| 383 | + if nws_hourly_periods: |
| 384 | + nws_hourly_forecast_info += "⏰ <b>NWS Hourly Forecast:</b>\n" |
| 385 | + for period in nws_hourly_periods[:3]: # Limit to next 3 hourly forecasts |
| 386 | + start_time = period.get('startTime', 'N/A') |
| 387 | + temperature = period.get('temperature', 'N/A') |
| 388 | + temperature_unit = period.get('temperatureUnit', 'N/A') |
| 389 | + wind_speed = period.get('windSpeed', 'N/A') |
| 390 | + wind_direction = period.get('windDirection', 'N/A') |
| 391 | + short_forecast = period.get('shortForecast', 'N/A') |
| 392 | + nws_hourly_forecast_info += f"{start_time}: {short_forecast}, {temperature}°{temperature_unit}, Wind: {wind_speed} {wind_direction}\n" |
| 393 | + else: |
| 394 | + nws_hourly_forecast_info += "⏰ <b>NWS Hourly Forecast:</b> Ei saatavilla.\n" |
| 395 | + else: |
| 396 | + nws_hourly_forecast_info = "⏰ <b>NWS Hourly Forecast:</b> Ei saatavilla.\n" |
| 397 | + |
| 398 | + combined_info += f"\n{nws_forecast_info}\n{nws_hourly_forecast_info}" |
| 399 | + |
| 400 | + # Fetch and append NWS Alerts |
| 401 | + try: |
| 402 | + # Round coordinates to 4 decimal places to comply with NWS API |
| 403 | + lat_rounded = round(lat, 4) |
| 404 | + lon_rounded = round(lon, 4) |
| 405 | + alerts_url = f"https://api.weather.gov/alerts/active?point={lat_rounded},{lon_rounded}" |
| 406 | + async with httpx.AsyncClient(follow_redirects=True) as client: |
| 407 | + alerts_response = await client. get( alerts_url, headers={ 'User-Agent': 'YourAppName ([email protected])'}) |
| 408 | + alerts_response.raise_for_status() |
| 409 | + alerts_data = alerts_response.json() |
| 410 | + except httpx.HTTPStatusError as e: |
| 411 | + logging.error(f"NWS Alerts HTTP error: {e.response.status_code} - {e.response.text}") |
| 412 | + alerts_data = None |
| 413 | + except Exception as e: |
| 414 | + logging.error(f"Error fetching NWS alerts: {e}") |
| 415 | + alerts_data = None |
| 416 | + |
| 417 | + alerts_info = "" |
| 418 | + if alerts_data and 'features' in alerts_data and alerts_data['features']: |
| 419 | + alerts_info += "[HUOM! HUOMIOI NÄMÄ! TAKE THESE INTO ACCOUNT!!! MENTION THESE TO THE USER IF THERE ARE WEATHER ALERTS -- INCLUDE ALL THE DETAILS. WHAT, WHEN, WHERE, WHAT SEVERITY, ETC.]\n🚨 <b>ONGOING ALERTS FROM THE U.S. NWS (weather.gov):</b>\n" |
| 420 | + for idx, alert in enumerate(alerts_data['features'], start=1): |
| 421 | + properties = alert.get('properties', {}) |
| 422 | + |
| 423 | + event = properties.get('event', 'EVENT').upper() |
| 424 | + headline = properties.get('headline', 'HEADLINE') |
| 425 | + instruction = properties.get('instruction', 'INSTRUCTION') |
| 426 | + severity = properties.get('severity', 'Unknown').capitalize() |
| 427 | + certainty = properties.get('certainty', 'Unknown').capitalize() |
| 428 | + urgency = properties.get('urgency', 'Unknown').capitalize() |
| 429 | + area_desc = properties.get('areaDesc', 'N/A') |
| 430 | + effective = properties.get('effective', 'N/A') |
| 431 | + expires = properties.get('expires', 'N/A') |
| 432 | + |
| 433 | + alerts_info += ( |
| 434 | + f"{idx}. ⚠️ <b>{event}</b>\n" |
| 435 | + f"<b>Vaara:</b> {headline}\n" |
| 436 | + f"<b>Ohjeet:</b> {instruction}\n" |
| 437 | + f"<b>Alue:</b> {area_desc}\n" |
| 438 | + f"<b>Vakavuus:</b> {severity}\n" |
| 439 | + f"<b>Varmuus:</b> {certainty}\n" |
| 440 | + f"<b>Kiireellisyys:</b> {urgency}\n" |
| 441 | + f"<b>Voimassa alkaen:</b> {effective}\n" |
| 442 | + f"<b>Päättyy:</b> {expires}\n\n" |
| 443 | + ) |
| 444 | + else: |
| 445 | + alerts_info += "\n🚨 Ei aktiivisia varoituksia U.S. NWS:n (weather.gov) mukaan.\n" |
| 446 | + |
| 447 | + # if alerts_data and 'features' in alerts_data and alerts_data['features']: |
| 448 | + # alerts_info += "\n🚨 <b>NWS ALERTS:</b>\n" |
| 449 | + # for alert in alerts_data['features']: |
| 450 | + # event = alert.get('properties', {}).get('event', 'EVENT').upper() |
| 451 | + # severity = alert.get('properties', {}).get('severity', 'SEVERITY').upper() |
| 452 | + # headline = alert.get('properties', {}).get('headline', 'HEADLINE') |
| 453 | + # instruction = alert.get('properties', {}).get('instruction', 'INSTRUCTION') |
| 454 | + |
| 455 | + # # Highlight severe alerts |
| 456 | + # if 'HURRICANE' in event or severity in ['WATCH', 'WARNING', 'EMERGENCY']: |
| 457 | + # alerts_info += f"🔥 <b>{event}</b>\n<b>Vaara:</b> {headline}\n<b>Ohjeet:</b> {instruction}\n\n" |
| 458 | + # else: |
| 459 | + # # Include less severe alerts if needed |
| 460 | + # alerts_info += f"<b>{event}</b>\n{headline}\n{instruction}\n\n" |
| 461 | + # else: |
| 462 | + # alerts_info += "\n🚨 <b>NWS ALERTS:</b> Ei aktiivisia varoituksia.\n" |
| 463 | + |
| 464 | + combined_info += alerts_info |
| 465 | + |
| 466 | + # Combine all information |
| 467 | + combined_info += f"\n{detailed_weather_info}\n\n{final_forecast}" |
| 468 | + |
327 | 469 | # Append additional data for Finland if available |
328 | 470 | if additional_data: |
329 | 471 | combined_info += f"\n\n[ Lisätiedot Suomeen (lähde: foreca.fi -- MAINITSE LÄHDE) ]\n{additional_data}" |
|
0 commit comments