2024-09-15 23:25:17 +02:00
|
|
|
package weather
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
2024-09-16 00:28:43 +02:00
|
|
|
"encoding/json"
|
|
|
|
"errors"
|
2024-09-15 23:25:17 +02:00
|
|
|
"fmt"
|
|
|
|
"github.com/carlmjohnson/requests"
|
2024-09-16 00:28:43 +02:00
|
|
|
"log"
|
2024-09-15 23:25:17 +02:00
|
|
|
"net/http"
|
2024-09-16 00:28:43 +02:00
|
|
|
"os"
|
|
|
|
"time"
|
2024-09-15 23:25:17 +02:00
|
|
|
)
|
|
|
|
|
|
|
|
type Config struct {
|
2024-09-16 00:28:43 +02:00
|
|
|
APIKey string
|
|
|
|
CacheLocation string
|
2024-09-15 23:25:17 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
type Client struct {
|
|
|
|
config Config
|
|
|
|
client *http.Client
|
|
|
|
}
|
|
|
|
|
|
|
|
func New(httpClient *http.Client, config Config) *Client {
|
|
|
|
return &Client{
|
|
|
|
config: config,
|
|
|
|
client: httpClient,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
type Prevision struct {
|
|
|
|
Lat float64 `json:"lat"`
|
|
|
|
Lon float64 `json:"lon"`
|
|
|
|
Timezone string `json:"timezone"`
|
|
|
|
TimezoneOffset int `json:"timezone_offset"`
|
|
|
|
Current struct {
|
|
|
|
Dt int `json:"dt"`
|
|
|
|
Sunrise int `json:"sunrise"`
|
|
|
|
Sunset int `json:"sunset"`
|
|
|
|
Temp float64 `json:"temp"`
|
|
|
|
FeelsLike float64 `json:"feels_like"`
|
|
|
|
Pressure int `json:"pressure"`
|
|
|
|
Humidity int `json:"humidity"`
|
|
|
|
DewPoint float64 `json:"dew_point"`
|
|
|
|
Uvi float64 `json:"uvi"`
|
|
|
|
Clouds int `json:"clouds"`
|
|
|
|
Visibility int `json:"visibility"`
|
|
|
|
WindSpeed float64 `json:"wind_speed"`
|
|
|
|
WindDeg int `json:"wind_deg"`
|
|
|
|
WindGust float64 `json:"wind_gust"`
|
|
|
|
Weather []struct {
|
|
|
|
Id int `json:"id"`
|
|
|
|
Main string `json:"main"`
|
|
|
|
Description string `json:"description"`
|
|
|
|
Icon string `json:"icon"`
|
|
|
|
} `json:"weather"`
|
|
|
|
} `json:"current"`
|
|
|
|
Daily []Daily `json:"daily"`
|
|
|
|
Alerts []struct {
|
|
|
|
SenderName string `json:"sender_name"`
|
|
|
|
Event string `json:"event"`
|
|
|
|
Start int `json:"start"`
|
|
|
|
End int `json:"end"`
|
|
|
|
Description string `json:"description"`
|
|
|
|
Tags []string `json:"tags"`
|
|
|
|
} `json:"alerts"`
|
|
|
|
}
|
|
|
|
|
2024-09-16 00:28:43 +02:00
|
|
|
var errTooOld = errors.New("prevision is too old")
|
|
|
|
|
|
|
|
func loadFromDisk(location string) (Prevision, error) {
|
|
|
|
stat, err := os.Stat(location)
|
|
|
|
if err != nil {
|
|
|
|
return Prevision{}, fmt.Errorf("getting file info: %w", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
if stat.ModTime().Add(10 * time.Minute).Before(time.Now()) {
|
|
|
|
return Prevision{}, errTooOld
|
|
|
|
}
|
|
|
|
|
|
|
|
file, err := os.Open(location)
|
|
|
|
if err != nil {
|
|
|
|
return Prevision{}, fmt.Errorf("opening prevision: %w", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
defer file.Close()
|
|
|
|
|
|
|
|
var res Prevision
|
|
|
|
if err = json.NewDecoder(file).Decode(&res); err != nil {
|
|
|
|
return Prevision{}, fmt.Errorf("decoding prevision: %w", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
return res, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (p Prevision) dumpToDisk(location string) error {
|
|
|
|
file, err := os.Create(location)
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("creating prevision: %w", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
defer file.Close()
|
|
|
|
|
|
|
|
if err = json.NewEncoder(file).Encode(p); err != nil {
|
|
|
|
return fmt.Errorf("dumping prevision: %w", err)
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2024-09-15 23:25:17 +02:00
|
|
|
type Daily struct {
|
|
|
|
Dt int `json:"dt"`
|
|
|
|
Sunrise int `json:"sunrise"`
|
|
|
|
Sunset int `json:"sunset"`
|
|
|
|
Moonrise int `json:"moonrise"`
|
|
|
|
Moonset int `json:"moonset"`
|
|
|
|
MoonPhase float64 `json:"moon_phase"`
|
|
|
|
Summary string `json:"summary"`
|
|
|
|
Temp struct {
|
|
|
|
Day float64 `json:"day"`
|
|
|
|
Min float64 `json:"min"`
|
|
|
|
Max float64 `json:"max"`
|
|
|
|
Night float64 `json:"night"`
|
|
|
|
Eve float64 `json:"eve"`
|
|
|
|
Morn float64 `json:"morn"`
|
|
|
|
} `json:"temp"`
|
|
|
|
FeelsLike struct {
|
|
|
|
Day float64 `json:"day"`
|
|
|
|
Night float64 `json:"night"`
|
|
|
|
Eve float64 `json:"eve"`
|
|
|
|
Morn float64 `json:"morn"`
|
|
|
|
} `json:"feels_like"`
|
|
|
|
Pressure int `json:"pressure"`
|
|
|
|
Humidity int `json:"humidity"`
|
|
|
|
DewPoint float64 `json:"dew_point"`
|
|
|
|
WindSpeed float64 `json:"wind_speed"`
|
|
|
|
WindDeg int `json:"wind_deg"`
|
|
|
|
WindGust float64 `json:"wind_gust"`
|
|
|
|
Weather []Weather `json:"weather"`
|
|
|
|
Clouds int `json:"clouds"`
|
|
|
|
Pop float64 `json:"pop"`
|
|
|
|
Rain float64 `json:"rain"`
|
|
|
|
Uvi float64 `json:"uvi"`
|
|
|
|
}
|
|
|
|
|
|
|
|
type Weather struct {
|
|
|
|
Id int `json:"id"`
|
|
|
|
Main string `json:"main"`
|
|
|
|
Description string `json:"description"`
|
|
|
|
Icon string `json:"icon"`
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *Client) GetWeather(ctx context.Context) (res *Prevision, err error) {
|
2024-09-16 00:28:43 +02:00
|
|
|
if val, err := loadFromDisk(c.config.CacheLocation); nil == err {
|
|
|
|
log.Println("found weather in cache")
|
|
|
|
return &val, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
log.Println("querying weather")
|
|
|
|
|
2024-09-15 23:25:17 +02:00
|
|
|
err = requests.URL("https://api.openweathermap.org/data/3.0/onecall").
|
|
|
|
Client(c.client).
|
|
|
|
Param("lat", "45.78").
|
|
|
|
Param("lon", "4.89").
|
|
|
|
Param("appid", c.config.APIKey).
|
|
|
|
Param("units", "metric").
|
|
|
|
Param("lang", "fr").
|
|
|
|
Param("exclude", "minutely,hourly").
|
|
|
|
ToJSON(&res).
|
|
|
|
Fetch(ctx)
|
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("calling openweathermap: %w", err)
|
|
|
|
}
|
|
|
|
|
2024-09-16 00:28:43 +02:00
|
|
|
if err := res.dumpToDisk(c.config.CacheLocation); err != nil {
|
|
|
|
log.Printf("error dumping files to disk: %v\n", err)
|
|
|
|
}
|
|
|
|
|
2024-09-15 23:25:17 +02:00
|
|
|
return res, nil
|
|
|
|
}
|