fix: use Open-Meteo instead of wttr.in (wttr.in response gets truncated in container)
All checks were successful
Deploy to Coolify / deploy (push) Successful in 4s

This commit is contained in:
Cosmo
2026-04-22 10:37:43 +00:00
parent 56d03ae781
commit a2fb233363

View File

@@ -1,63 +1,84 @@
export const dynamic = 'force-dynamic'; export const dynamic = 'force-dynamic';
import { NextResponse } from "next/server"; import { NextResponse } from "next/server";
function getWeatherCode(desc: string): string { // Convert WMO weather codes to wttr.in-compatible codes for emoji mapping
const d = desc.toLowerCase(); function wmoToWttrCode(wmo: number): string {
if (d.includes("overcast")) return "122"; if (wmo === 0) return "113"; // Clear sunny
if (d.includes("partly cloudy") || d.includes("partly") ) return "116"; if (wmo === 1) return "116"; // Mainly clear
if (d.includes("cloudy") || d.includes("cloud")) return "119"; if (wmo === 2) return "116"; // Partly cloudy
if (d.includes("drizzle")) return "185"; if (wmo === 3) return "122"; // Overcast
if (d.includes("rain") || d.includes("shower")) return "305"; if (wmo >= 45 && wmo <= 48) return "143"; // Fog
if (d.includes("snow") || d.includes("blizzard")) return "230"; if (wmo >= 51 && wmo <= 55) return "185"; // Drizzle
if (d.includes("thunder") || d.includes("storm")) return "200"; if (wmo >= 56 && wmo <= 57) return "281"; // Freezing drizzle
if (d.includes("fog") || d.includes("mist")) return "248"; if (wmo >= 61 && wmo <= 65) return "305"; // Rain
if (d.includes("sunny") || d.includes("clear")) return "113"; if (wmo >= 66 && wmo <= 67) return "281"; // Freezing rain
if (wmo >= 71 && wmo <= 77) return "227"; // Snow
if (wmo >= 80 && wmo <= 82) return "305"; // Rain showers
if (wmo >= 85 && wmo <= 86) return "260"; // Snow showers
if (wmo === 95) return "200"; // Thunderstorm
if (wmo >= 96 && wmo <= 99) return "389"; // Thunderstorm with hail
return "116"; return "116";
} }
function wmoToDesc(wmo: number): string {
if (wmo === 0) return "Ясно";
if (wmo === 1) return "Преим. ясно";
if (wmo === 2) return "Переменная облачность";
if (wmo === 3) return "Пасмурно";
if (wmo === 45 || wmo === 48) return "Туман";
if (wmo >= 51 && wmo <= 55) return "Морось";
if (wmo >= 61 && wmo <= 65) return "Дождь";
if (wmo >= 71 && wmo <= 77) return "Снег";
if (wmo >= 80 && wmo <= 82) return "Ливень";
if (wmo >= 85 && wmo <= 86) return "Снегопад";
if (wmo === 95) return "Гроза";
if (wmo >= 96 && wmo <= 99) return "Гроза с градом";
return "Облачно";
}
export async function GET() { export async function GET() {
try { try {
const controller = new AbortController(); const controller = new AbortController();
const timeout = setTimeout(() => controller.abort(), 8000); const timeout = setTimeout(() => controller.abort(), 8000);
const res = await fetch( const url = "https://api.open-meteo.com/v1/forecast?" + new URLSearchParams({
"https://wttr.in/Saint+Petersburg?format=j1", latitude: "59.9343",
{ longitude: "30.3351",
signal: controller.signal, current: "temperature_2m,relative_humidity_2m,apparent_temperature,weather_code,wind_speed_10m",
cache: "no-store", daily: "weather_code,temperature_2m_max,temperature_2m_min",
headers: { timezone: "Europe/Moscow",
"User-Agent": "SmartHomeDashboard/1.0", forecast_days: "3",
"Accept": "application/json",
},
}
);
clearTimeout(timeout);
if (!res.ok) throw new Error(`wttr responded ${res.status}`);
const data = await res.json();
const current = data.current_condition[0];
const days = (data.weather || []).slice(0, 3).map((day: any) => {
const hourly = day.hourly?.[4] || day.hourly?.[0] || {};
const desc = hourly.weatherDesc?.[0]?.value || "";
const code = hourly.weatherCode || getWeatherCode(desc);
return {
date: day.date,
maxTemp: day.maxtempC,
minTemp: day.mintempC,
desc,
weatherCode: String(code),
};
}); });
const res = await fetch(url, {
signal: controller.signal,
cache: "no-store",
headers: { "Accept": "application/json" },
});
clearTimeout(timeout);
if (!res.ok) throw new Error(`Open-Meteo responded ${res.status}`);
const data = await res.json();
const current = data.current;
const daily = data.daily;
const forecast = (daily.time || []).slice(0, 3).map((date: string, i: number) => ({
date,
maxTemp: String(Math.round(daily.temperature_2m_max[i])),
minTemp: String(Math.round(daily.temperature_2m_min[i])),
desc: wmoToDesc(daily.weather_code[i]),
weatherCode: wmoToWttrCode(daily.weather_code[i]),
}));
return NextResponse.json({ return NextResponse.json({
temp: current.temp_C, temp: String(Math.round(current.temperature_2m)),
feelsLike: current.FeelsLikeC, feelsLike: String(Math.round(current.apparent_temperature)),
humidity: current.humidity, humidity: String(current.relative_humidity_2m),
desc: current.weatherDesc[0]?.value || "", desc: wmoToDesc(current.weather_code),
weatherCode: String(current.weatherCode), weatherCode: wmoToWttrCode(current.weather_code),
windSpeed: current.windspeedKmph, windSpeed: String(Math.round(current.wind_speed_10m)),
forecast: days, forecast,
}); });
} catch (e) { } catch (e) {
console.error("Weather fetch error:", e); console.error("Weather fetch error:", e);