51 lines
1.5 KiB
TypeScript
51 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) },
|
||
|
|
};
|
||
|
|
}
|