127 lines
3.2 KiB
Go
127 lines
3.2 KiB
Go
package weather
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"io"
|
|
"net/http"
|
|
"time"
|
|
)
|
|
|
|
// WeatherData contains current weather conditions
|
|
type WeatherData struct {
|
|
Temperature float64 `json:"temp"`
|
|
FeelsLike float64 `json:"feels_like"`
|
|
Humidity int `json:"humidity"`
|
|
Pressure int `json:"pressure"`
|
|
WindSpeed float64 `json:"wind_speed"`
|
|
WindGust float64 `json:"wind_gust"`
|
|
WindDeg int `json:"wind_deg"`
|
|
Clouds int `json:"clouds"`
|
|
Description string `json:"description"`
|
|
Icon string `json:"icon"`
|
|
UpdatedAt string `json:"updated"`
|
|
}
|
|
|
|
// OpenWeatherMapResponse matches the API response
|
|
type OpenWeatherMapResponse struct {
|
|
Weather []struct {
|
|
Description string `json:"description"`
|
|
Icon string `json:"icon"`
|
|
} `json:"weather"`
|
|
Main struct {
|
|
Temp float64 `json:"temp"`
|
|
FeelsLike float64 `json:"feels_like"`
|
|
Humidity int `json:"humidity"`
|
|
Pressure int `json:"pressure"`
|
|
} `json:"main"`
|
|
Wind struct {
|
|
Speed float64 `json:"speed"`
|
|
Deg int `json:"deg"`
|
|
Gust float64 `json:"gust"`
|
|
} `json:"wind"`
|
|
Clouds struct {
|
|
All int `json:"all"`
|
|
} `json:"clouds"`
|
|
Dt int64 `json:"dt"`
|
|
}
|
|
|
|
type Client struct {
|
|
apiKey string
|
|
latitude float64
|
|
longitude float64
|
|
httpClient *http.Client
|
|
lastUpdate time.Time
|
|
cachedData *WeatherData
|
|
}
|
|
|
|
func New(apiKey string, latitude, longitude float64) *Client {
|
|
return &Client{
|
|
apiKey: apiKey,
|
|
latitude: latitude,
|
|
longitude: longitude,
|
|
httpClient: &http.Client{
|
|
Timeout: 10 * time.Second,
|
|
},
|
|
}
|
|
}
|
|
|
|
// GetWeatherData fetches current weather from OpenWeatherMap
|
|
// Data is cached for 10 minutes
|
|
func (c *Client) GetWeatherData() (*WeatherData, error) {
|
|
// Return cached data if less than 10 minutes old
|
|
if c.cachedData != nil && time.Since(c.lastUpdate) < 10*time.Minute {
|
|
return c.cachedData, nil
|
|
}
|
|
|
|
url := fmt.Sprintf(
|
|
"https://api.openweathermap.org/data/2.5/weather?lat=%f&lon=%f&appid=%s&units=metric",
|
|
c.latitude, c.longitude, c.apiKey,
|
|
)
|
|
|
|
resp, err := c.httpClient.Get(url)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to fetch weather data: %w", err)
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
if resp.StatusCode != http.StatusOK {
|
|
body, _ := io.ReadAll(resp.Body)
|
|
return nil, fmt.Errorf("unexpected status code %d: %s", resp.StatusCode, string(body))
|
|
}
|
|
|
|
body, err := io.ReadAll(resp.Body)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to read response: %w", err)
|
|
}
|
|
|
|
var owmData OpenWeatherMapResponse
|
|
if err := json.Unmarshal(body, &owmData); err != nil {
|
|
return nil, fmt.Errorf("failed to parse JSON: %w", err)
|
|
}
|
|
|
|
// Convert to our structure
|
|
weatherData := &WeatherData{
|
|
Temperature: owmData.Main.Temp,
|
|
FeelsLike: owmData.Main.FeelsLike,
|
|
Humidity: owmData.Main.Humidity,
|
|
Pressure: owmData.Main.Pressure,
|
|
WindSpeed: owmData.Wind.Speed,
|
|
WindGust: owmData.Wind.Gust,
|
|
WindDeg: owmData.Wind.Deg,
|
|
Clouds: owmData.Clouds.All,
|
|
UpdatedAt: time.Unix(owmData.Dt, 0).Format(time.RFC3339),
|
|
}
|
|
|
|
if len(owmData.Weather) > 0 {
|
|
weatherData.Description = owmData.Weather[0].Description
|
|
weatherData.Icon = owmData.Weather[0].Icon
|
|
}
|
|
|
|
// Cache the data
|
|
c.cachedData = weatherData
|
|
c.lastUpdate = time.Now()
|
|
|
|
return weatherData, nil
|
|
}
|