init
This commit is contained in:
commit
6250d23297
53 changed files with 6381 additions and 0 deletions
144
lib/weather/types.ts
Normal file
144
lib/weather/types.ts
Normal file
|
|
@ -0,0 +1,144 @@
|
|||
/**
|
||||
* The one shape the UI understands.
|
||||
*
|
||||
* Every provider returns wildly different JSON. Each adapter in ./providers
|
||||
* translates its provider's response into these types, so the UI never has
|
||||
* to know which provider it is talking to.
|
||||
*
|
||||
* Rules every adapter follows:
|
||||
* - Units are metric: °C, km/h, hPa, km, mm.
|
||||
* - Times are ISO-8601 instants in UTC ("2026-10-21T23:00:00.000Z").
|
||||
* The UI formats them in the location's time zone.
|
||||
* - A reading the provider doesn't offer is `null`, and the UI hides it.
|
||||
*/
|
||||
|
||||
export type ProviderId = "openweather" | "weatherapi" | "tomorrow" | "openmeteo";
|
||||
|
||||
/** A deliberately small set of conditions — one per pixel icon. */
|
||||
export type Condition = "clear" | "clouds" | "rain" | "storm" | "snow" | "fog";
|
||||
|
||||
export interface Place {
|
||||
name: string;
|
||||
region?: string;
|
||||
country?: string;
|
||||
lat: number;
|
||||
lon: number;
|
||||
}
|
||||
|
||||
export interface CurrentWeather {
|
||||
time: string;
|
||||
tempC: number;
|
||||
feelsLikeC: number;
|
||||
condition: Condition;
|
||||
description: string; // human words, e.g. "light rain"
|
||||
isDay: boolean;
|
||||
humidity: number; // %
|
||||
windKph: number;
|
||||
gustKph: number | null;
|
||||
windDeg: number | null; // direction the wind blows FROM, 0 = north
|
||||
pressureHpa: number | null;
|
||||
dewPointC: number | null;
|
||||
visibilityKm: number | null;
|
||||
uvIndex: number | null;
|
||||
cloudCover: number | null; // %
|
||||
precipMm: number | null; // in the last hour
|
||||
}
|
||||
|
||||
export interface HourlyForecast {
|
||||
time: string; // start of the hour (or 3-hour block)
|
||||
tempC: number;
|
||||
feelsLikeC: number | null;
|
||||
condition: Condition;
|
||||
isDay: boolean;
|
||||
precipChance: number | null; // %
|
||||
precipMm: number | null;
|
||||
windKph: number;
|
||||
gustKph: number | null;
|
||||
windDeg: number | null;
|
||||
humidity: number | null;
|
||||
uvIndex: number | null;
|
||||
}
|
||||
|
||||
export interface DailyForecast {
|
||||
time: string; // any instant inside that local day (midday where possible)
|
||||
highC: number;
|
||||
lowC: number;
|
||||
condition: Condition;
|
||||
summary: string | null; // provider's own one-liner, if it has one
|
||||
precipChance: number | null;
|
||||
precipMm: number | null;
|
||||
windKph: number | null; // daily max
|
||||
gustKph: number | null;
|
||||
windDeg: number | null;
|
||||
uvIndexMax: number | null;
|
||||
humidity: number | null; // daily average
|
||||
sunrise: string | null;
|
||||
sunset: string | null;
|
||||
}
|
||||
|
||||
export type AlertSeverity = "extreme" | "severe" | "moderate" | "minor" | "unknown";
|
||||
|
||||
export interface WeatherAlert {
|
||||
title: string;
|
||||
severity: AlertSeverity;
|
||||
source: string; // who issued it, e.g. "NWS Twin Cities/Chanhassen MN"
|
||||
starts: string | null;
|
||||
ends: string | null;
|
||||
description: string;
|
||||
instruction: string | null;
|
||||
}
|
||||
|
||||
export interface AirQuality {
|
||||
usAqi: number; // US EPA scale, 0–500
|
||||
pm25: number | null; // µg/m³
|
||||
pm10: number | null;
|
||||
ozone: number | null;
|
||||
no2: number | null;
|
||||
}
|
||||
|
||||
/** What one provider adapter returns. */
|
||||
export interface ProviderForecast {
|
||||
current: CurrentWeather;
|
||||
hourly: HourlyForecast[];
|
||||
daily: DailyForecast[];
|
||||
hourlyStepHours: 1 | 3;
|
||||
timeZone: string | null; // IANA name, if the provider tells us
|
||||
alerts: WeatherAlert[];
|
||||
}
|
||||
|
||||
/** What /api/weather returns: the provider's forecast plus extras from other sources. */
|
||||
export interface WeatherReport extends ProviderForecast {
|
||||
provider: ProviderId;
|
||||
timeZone: string | null;
|
||||
airQuality: AirQuality | null;
|
||||
alertSource: string | null;
|
||||
fetchedAt: string;
|
||||
}
|
||||
|
||||
/** The contract each provider adapter fulfils. */
|
||||
export interface WeatherProvider {
|
||||
id: ProviderId;
|
||||
label: string;
|
||||
/** Name of the env var holding its key, or null if no key is needed. */
|
||||
envKey: string | null;
|
||||
signupUrl: string;
|
||||
/** What this provider can deliver on the plan configured in .env.local. */
|
||||
limits(): { forecastDays: number; hourlyStepHours: 1 | 3 };
|
||||
getForecast(lat: number, lon: number): Promise<ProviderForecast>;
|
||||
}
|
||||
|
||||
/** Safe-to-share provider info for the browser (never includes keys). */
|
||||
export interface ProviderInfo {
|
||||
id: ProviderId;
|
||||
label: string;
|
||||
ready: boolean;
|
||||
signupUrl: string;
|
||||
forecastDays: number;
|
||||
hourlyStepHours: 1 | 3;
|
||||
}
|
||||
|
||||
/** Map overlays the radar panel can show. */
|
||||
export interface RadarFrame {
|
||||
time: string;
|
||||
url: string; // Leaflet tile template with {z}/{x}/{y}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue