rot finished

This commit is contained in:
2026-01-09 11:56:40 +01:00
parent 1ee0afa088
commit ac99f291a7
30 changed files with 5581 additions and 293 deletions

View File

@@ -0,0 +1,126 @@
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
}