wayfarer-weather/lib/weather/extras/air-quality.ts
Cayden Schwechten 6250d23297 init
2026-10-31 21:20:16 -05:00

50 lines
1.5 KiB
TypeScript

import { fetchJson, maybe } from "../http";
import type { AirQuality } from "../types";
/**
* Air quality from Open-Meteo's free air-quality API (CAMS data), used for
* every provider so the AQI always means the same thing.
* Docs: https://open-meteo.com/en/docs/air-quality-api
*
* The same call also tells us the location's IANA time zone, which some
* providers (Tomorrow.io, OpenWeather free) don't give us.
*/
interface AirQualityResponse {
timezone: string;
current: {
us_aqi: number | null;
pm2_5: number | null;
pm10: number | null;
ozone: number | null;
nitrogen_dioxide: number | null;
};
}
export interface LocalContext {
timeZone: string;
airQuality: AirQuality | null;
}
export async function getLocalContext(lat: number, lon: number): Promise<LocalContext> {
const params = new URLSearchParams({
latitude: String(lat),
longitude: String(lon),
current: "us_aqi,pm2_5,pm10,ozone,nitrogen_dioxide",
timezone: "auto",
});
const data = await fetchJson<AirQualityResponse>(
`https://air-quality-api.open-meteo.com/v1/air-quality?${params}`,
"Open-Meteo air quality",
{ cacheSeconds: 1800 }, // air quality updates hourly at most
);
const c = data.current;
return {
timeZone: data.timezone,
airQuality:
c.us_aqi === null
? null
: { usAqi: Math.round(c.us_aqi), pm25: maybe(c.pm2_5), pm10: maybe(c.pm10), ozone: maybe(c.ozone), no2: maybe(c.nitrogen_dioxide) },
};
}