UNPKG

rui-weather-service-api

Version:

Weather API service with OpenWeatherMap integration - includes both standalone API and MCP server

180 lines 6.65 kB
import { fetchWithRetry } from '../utils/http-client.js'; import config, { updateConfig, validateConfig } from '../config/index.js'; /** * Weather API Client for OpenWeatherMap */ export class WeatherApiClient { /** * Creates a new WeatherApiClient instance * @param options Configuration options */ constructor(options = {}) { // Update configuration with provided options if (Object.keys(options).length > 0) { updateConfig(options); } } /** * Gets current weather for a location * @param city City name * @returns Weather data */ async getCurrentWeather(city) { // Validate inputs this.validateInputs(city); console.log(`Fetching current weather for ${city}...`); const params = { q: city, appid: config.apiKey, units: config.units, lang: config.language }; // Fetch data with retry capability const weatherData = await fetchWithRetry('/weather', params); // Map API response to our structured format return { location: { name: weatherData.name, country: weatherData.sys.country, coordinates: { lat: weatherData.coord.lat, lon: weatherData.coord.lon } }, temperature: { current: weatherData.main.temp, feelsLike: weatherData.main.feels_like, min: weatherData.main.temp_min, max: weatherData.main.temp_max }, weather: { main: weatherData.weather[0].main, description: weatherData.weather[0].description, icon: weatherData.weather[0].icon }, wind: { speed: weatherData.wind.speed, direction: weatherData.wind.deg }, details: { humidity: weatherData.main.humidity, pressure: weatherData.main.pressure, visibility: weatherData.visibility / 1000, sunrise: new Date(weatherData.sys.sunrise * 1000), sunset: new Date(weatherData.sys.sunset * 1000) }, updatedAt: new Date(weatherData.dt * 1000), raw: weatherData }; } /** * Gets weather forecast for a location * @param city City name * @param days Number of days (1-5) * @returns Forecast data */ async getForecast(city, days = 5) { // Validate inputs this.validateInputs(city); // Validate days parameter if (isNaN(days) || days < 1 || days > 5) { throw new Error('Days parameter must be between 1 and 5'); } console.log(`Fetching ${days}-day forecast for ${city}...`); const params = { q: city, appid: config.apiKey, units: config.units, lang: config.language, cnt: Math.min(days * 8, 40), // Each day has 8 forecasts (every 3 hours), max 5 days }; // Fetch data with retry capability const forecastData = await fetchWithRetry('/forecast', params); // Process forecast data by day const dailyForecasts = {}; forecastData.list.forEach((item) => { const date = new Date(item.dt * 1000).toLocaleDateString(); if (!dailyForecasts[date]) { dailyForecasts[date] = { date: new Date(item.dt * 1000), temperature: { min: item.main.temp, max: item.main.temp, avg: item.main.temp }, weather: { main: item.weather[0].main, description: item.weather[0].description, icon: item.weather[0].icon }, wind: { speed: item.wind.speed, direction: item.wind.deg }, details: { humidity: item.main.humidity, pressure: item.main.pressure }, hourlyForecasts: [] }; } else { // Update min/max temperatures dailyForecasts[date].temperature.min = Math.min(dailyForecasts[date].temperature.min, item.main.temp); dailyForecasts[date].temperature.max = Math.max(dailyForecasts[date].temperature.max, item.main.temp); } // Add hourly forecast dailyForecasts[date].hourlyForecasts.push({ time: new Date(item.dt * 1000), temperature: item.main.temp, feelsLike: item.main.feels_like, weather: { main: item.weather[0].main, description: item.weather[0].description, icon: item.weather[0].icon }, wind: { speed: item.wind.speed, direction: item.wind.deg }, details: { humidity: item.main.humidity, pressure: item.main.pressure } }); }); // Calculate average temperatures Object.keys(dailyForecasts).forEach(date => { const forecasts = dailyForecasts[date].hourlyForecasts; const sum = forecasts.reduce((acc, curr) => acc + curr.temperature, 0); dailyForecasts[date].temperature.avg = sum / forecasts.length; }); return { city: { name: forecastData.city.name, country: forecastData.city.country, coordinates: { lat: forecastData.city.coord.lat, lon: forecastData.city.coord.lon } }, forecasts: Object.values(dailyForecasts), raw: forecastData }; } /** * Validates input parameters * @param city City name */ validateInputs(city) { // Validate city parameter if (!city || city.trim() === '') { throw new Error('City name is required'); } // Validate API key validateConfig(); } } // Export a default instance for convenience export const weatherApi = new WeatherApiClient(); //# sourceMappingURL=weather-api.js.map