ws4kp-linhanced/src/weather-parameters.mjs
mrkmntal dbb32fd2f9
Some checks are pending
build-docker / Build Image (push) Waiting to run
v0.2.1! Adds instant Hazard List updates after hazard sync
2026-04-19 13:50:02 -04:00

86 lines
2.9 KiB
JavaScript

import { aggregateWeatherForecastData } from '../server/scripts/modules/utils/weather.mjs';
const REQUEST_TIMEOUT_MS = 10000;
const USER_AGENT = 'WeatherStar 4000+: Linhanced; marky611@gmail.com';
const OPEN_METEO_FORECAST_PARAMETERS = [
'daily=temperature_2m_max,temperature_2m_min,uv_index_max',
'hourly=temperature_2m,relative_humidity_2m,dew_point_2m,apparent_temperature,precipitation_probability,precipitation,rain,showers,snowfall,snow_depth,weather_code,pressure_msl,surface_pressure,cloud_cover,visibility,uv_index,is_day,sunshine_duration,wind_speed_10m,wind_direction_10m,wind_gusts_10m',
'timezone=auto',
'models=best_match',
].join('&');
const parseLocationKey = (locationKey) => {
if (typeof locationKey !== 'string') return null;
const [latText, lonText] = locationKey.split(',');
const lat = Number.parseFloat(latText);
const lon = Number.parseFloat(lonText);
if (!Number.isFinite(lat) || !Number.isFinite(lon)) return null;
return { lat, lon };
};
const splitLocationLabel = (locationLabel = '') => {
const [cityPart = 'Unknown', regionPart = ''] = locationLabel.split(',').map((part) => part.trim());
const isUsStyleState = /^[A-Z]{2}$/.test(regionPart);
return {
city: cityPart || 'Unknown',
state: isUsStyleState ? regionPart : '',
country: isUsStyleState ? 'United States' : regionPart,
countryCode: isUsStyleState ? 'US' : '',
};
};
const fetchJson = async (url) => {
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), REQUEST_TIMEOUT_MS);
try {
const response = await fetch(url, {
headers: {
'User-Agent': USER_AGENT,
},
signal: controller.signal,
});
if (!response.ok) {
throw new Error(`Fetch error ${response.status} ${response.statusText} while fetching ${url}`);
}
return await response.json();
} finally {
clearTimeout(timeoutId);
}
};
const buildWeatherParametersForLocation = async ({ locationKey, locationLabel }) => {
const coordinates = parseLocationKey(locationKey);
if (!coordinates) {
throw new Error(`Invalid location key '${locationKey}' for derived hazard refresh`);
}
const { lat, lon } = coordinates;
const locationParts = splitLocationLabel(locationLabel);
const forecast = await fetchJson(`https://api.open-meteo.com/v1/forecast?latitude=${lat}&longitude=${lon}&${OPEN_METEO_FORECAST_PARAMETERS}`);
const aggregatedForecast = aggregateWeatherForecastData(forecast);
if (!aggregatedForecast) {
throw new Error(`Unable to aggregate forecast for ${locationKey}`);
}
return {
latitude: lat,
longitude: lon,
city: locationParts.city,
state: locationParts.state,
country: locationParts.country,
countryCode: locationParts.countryCode,
timeZone: forecast?.timezone || 'UTC',
forecast: aggregatedForecast,
supportsNoaaAlerts: false,
primaryForecastSource: 'open-meteo',
};
};
export {
buildWeatherParametersForLocation,
parseLocationKey,
splitLocationLabel,
};