Files
smart-speaker/app/features/weather.py

144 lines
5.6 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""
Weather feature module.
Fetches weather data from Open-Meteo API.
"""
import requests
from datetime import datetime
from ..core.config import WEATHER_LAT, WEATHER_LON, WEATHER_CITY
def get_wmo_description(code: int) -> str:
"""Decodes WMO weather code to Russian description."""
codes = {
0: "ясно",
1: "преимущественно ясно",
2: "переменная облачность",
3: "пасмурно",
45: "туман",
48: "изморозь",
51: "легкая морось",
53: "умеренная морось",
55: "плотная морось",
56: "ледяная морось",
57: "плотная ледяная морось",
61: "слабый дождь",
63: "умеренный дождь",
65: "сильный дождь",
66: "ледяной дождь",
67: "сильный ледяной дождь",
71: "слабый снег",
73: "снегопад",
75: "сильный снегопад",
77: "снежные зерна",
80: "слабый ливень",
81: "умеренный ливень",
82: "сильный ливень",
85: "слабый снегопад",
86: "сильный снегопад",
95: "гроза",
96: "гроза с градом",
99: "сильная гроза с градом"
}
return codes.get(code, "осадки")
def get_weather_report() -> str:
"""
Fetches detailed weather report.
Structure:
1. Current temp and precipitation.
2. Today's min/max temp.
3. Next 4 hours forecast (temp + precipitation).
"""
if not all([WEATHER_LAT, WEATHER_LON, WEATHER_CITY]):
return "Настройки погоды не найдены. Проверьте конфигурацию."
url = "https://api.open-meteo.com/v1/forecast"
params = {
"latitude": WEATHER_LAT,
"longitude": WEATHER_LON,
"current": "temperature_2m,precipitation,weather_code",
"hourly": "temperature_2m,precipitation,weather_code",
"daily": "temperature_2m_max,temperature_2m_min",
"timezone": "auto",
"forecast_days": 2
}
try:
response = requests.get(url, params=params, timeout=5)
response.raise_for_status()
data = response.json()
# --- 1. Current Weather ---
curr = data["current"]
temp_now = round(curr["temperature_2m"])
precip_now = curr["precipitation"]
code_now = curr["weather_code"]
desc_now = get_wmo_description(code_now)
report = f"Сейчас в городе {WEATHER_CITY} {temp_now} градусов, {desc_now}."
if precip_now > 0:
report += f" Выпало {precip_now} миллиметров осадков."
# --- 2. Today's Range ---
# daily arrays usually start from today [0]
daily = data["daily"]
t_max = round(daily["temperature_2m_max"][0])
t_min = round(daily["temperature_2m_min"][0])
report += f" Сегодня температура будет от {t_min} до {t_max} градусов."
# --- 3. Forecast Next 4 Hours ---
current_hour = datetime.now().hour
hourly_temps = data["hourly"]["temperature_2m"]
hourly_precip = data["hourly"]["precipitation"]
hourly_codes = data["hourly"]["weather_code"]
# Start from next hour
start_idx = current_hour + 1
end_idx = min(start_idx + 4, len(hourly_temps))
next_temps = hourly_temps[start_idx:end_idx]
next_precip = hourly_precip[start_idx:end_idx]
next_codes = hourly_codes[start_idx:end_idx]
if next_temps:
report += " Прогноз на ближайшие 4 часа: "
# Group by roughly similar weather to avoid repetition?
# Or just list them simply.
# "В 14:00 -5, ясно. В 15:00 -5, снег." -> a bit verbose.
# Simplified: "Температура около -5, возможен слабый снег."
# Let's verify if weather changes significantly.
# If consistent, summarize. If not, list.
# Simple approach for TTS:
avg_temp = round(sum(next_temps) / len(next_temps))
# Check if any precipitation is expected
will_precip = any(p > 0 for p in next_precip)
unique_codes = set(next_codes)
# Determine dominant weather description
if len(unique_codes) == 1:
weather_desc = get_wmo_description(list(unique_codes)[0])
else:
# Priority to precipitation codes
precip_codes = [c for c in unique_codes if c > 3] # >3 implies not clear/cloudy
if precip_codes:
weather_desc = get_wmo_description(max(precip_codes)) # Take the most severe
else:
weather_desc = "переменная облачность"
report += f"температура около {avg_temp} градусов, {weather_desc}."
if will_precip:
report += " Ожидаются осадки."
else:
report += " Без существенных осадков."
return report
except Exception as e:
print(f"❌ Ошибка получения погоды: {e}")
return "Не удалось получить полные данные о погоде."