All checks were successful
Deploy to Coolify / deploy (push) Successful in 5s
- Remove TasksCard and SavingsCard from home tab - New grid layout: lights+thermostat row 1, purifier+weather row 2 - Add RoomsRow component with room navigation - Fix HA entity mapping: fan.zhimi_rmb1_9528_air_purifier → fan.air_purifier - Add real entity aliases for HA route - Fix weather route: add timeout, better error handling - Fix BottomNav: use 100dvh + flex-shrink-0 - TopBar: accept isDemo prop, show Demo badge in header - WeatherCard: compact prop, better loading/error states - globals.css: add no-scrollbar utility
79 lines
2.4 KiB
TypeScript
79 lines
2.4 KiB
TypeScript
export const dynamic = 'force-dynamic';
|
|
import { NextResponse } from "next/server";
|
|
|
|
function getWeatherCode(desc: string): string {
|
|
const d = desc.toLowerCase();
|
|
if (d.includes("overcast")) return "122";
|
|
if (d.includes("partly cloudy") || d.includes("partly") ) return "116";
|
|
if (d.includes("cloudy") || d.includes("cloud")) return "119";
|
|
if (d.includes("drizzle")) return "185";
|
|
if (d.includes("rain") || d.includes("shower")) return "305";
|
|
if (d.includes("snow") || d.includes("blizzard")) return "230";
|
|
if (d.includes("thunder") || d.includes("storm")) return "200";
|
|
if (d.includes("fog") || d.includes("mist")) return "248";
|
|
if (d.includes("sunny") || d.includes("clear")) return "113";
|
|
return "116";
|
|
}
|
|
|
|
export async function GET() {
|
|
try {
|
|
const controller = new AbortController();
|
|
const timeout = setTimeout(() => controller.abort(), 8000);
|
|
|
|
const res = await fetch(
|
|
"https://wttr.in/Saint+Petersburg?format=j1",
|
|
{
|
|
signal: controller.signal,
|
|
cache: "no-store",
|
|
headers: {
|
|
"User-Agent": "SmartHomeDashboard/1.0",
|
|
"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),
|
|
};
|
|
});
|
|
|
|
return NextResponse.json({
|
|
temp: current.temp_C,
|
|
feelsLike: current.FeelsLikeC,
|
|
humidity: current.humidity,
|
|
desc: current.weatherDesc[0]?.value || "",
|
|
weatherCode: String(current.weatherCode),
|
|
windSpeed: current.windspeedKmph,
|
|
forecast: days,
|
|
});
|
|
} catch (e) {
|
|
console.error("Weather fetch error:", e);
|
|
return NextResponse.json(
|
|
{
|
|
temp: "—",
|
|
feelsLike: "—",
|
|
humidity: "—",
|
|
desc: "Нет данных",
|
|
weatherCode: "116",
|
|
windSpeed: "—",
|
|
forecast: [],
|
|
error: String(e),
|
|
},
|
|
{ status: 200 }
|
|
);
|
|
}
|
|
}
|