aboutsummaryrefslogtreecommitdiff
path: root/liust-50/cmd/liustatus/weather.go
blob: c744f2f73f180b2a516121e541979c165d2f2296 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
package main

import (
	"encoding/xml"
	"fmt"
	"io"
	"log"
	"net/http"
	"strconv"
	"time"
)

const (
	baseURL   = "https://api.met.no/weatherapi"
	userAgent = "liustatus/1.0"

	// Prague coordinates.
	lat      = 50.08804
	lon      = 14.42076
	altitude = 202
)

// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

type Weatherdata struct {
	XMLName xml.Name `xml:"weatherdata"`
	Product Product  `xml:"product"`
}

type Product struct {
	Times []Time `xml:"time"`
}

type Time struct {
	From     string   `xml:"from,attr"`
	To       string   `xml:"to,attr"`
	Location Location `xml:"location"`
}

type Location struct {
	Temperature *Temperature `xml:"temperature"`
}

type Temperature struct {
	Unit  string `xml:"unit,attr"`
	Value string `xml:"value,attr"`
}

// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

// WeatherFetcher handles weather data retrieval.
type WeatherFetcher struct {
	client *http.Client
}

// NewWeatherFetcher creates a new weather fetcher instance.
func NewWeatherFetcher() *WeatherFetcher {
	return &WeatherFetcher{
		client: &http.Client{Timeout: 30 * time.Second},
	}
}

// fetchWeather retrieves the current temperature from the API.
func (w *WeatherFetcher) fetchWeather() (string, error) {
	url := fmt.Sprintf(
		"%s/locationforecast/2.0/classic?lat=%.5f&lon=%.5f&altitude=%d",
		baseURL, lat, lon, altitude)

	req, err := http.NewRequest("GET", url, nil)
	if err != nil {
		return "", err
	}

	req.Header.Set("User-Agent", userAgent)

	resp, err := w.client.Do(req)
	if err != nil {
		return "", err
	}
	defer resp.Body.Close()

	if resp.StatusCode != http.StatusOK {
		return "", fmt.Errorf("API returned status %d", resp.StatusCode)
	}

	body, err := io.ReadAll(resp.Body)
	if err != nil {
		return "", err
	}

	var weatherData Weatherdata
	if err := xml.Unmarshal(body, &weatherData); err != nil {
		return "", err
	}

	now := time.Now().UTC()
	for _, t := range weatherData.Product.Times {
		toTime, err := time.Parse("2006-01-02T15:04:05Z", t.To)
		if err != nil || toTime.Before(now) {
			continue
		}
		if t.Location.Temperature != nil {
			temp, err := strconv.ParseFloat(t.Location.Temperature.Value, 64)
			if err != nil {
				continue
			}
			return fmt.Sprintf("%d゚", int(temp)), nil
		}
	}

	return "", fmt.Errorf("no usable temperature data found")
}

// update fetches new weather data and returns it.
func (w *WeatherFetcher) update() string {
	temp, err := w.fetchWeather()
	if err != nil {
		log.Printf("Error fetching weather: %v", err)
	}
	return temp
}

// Run runs as a goroutine to periodically fetch weather data.
func (w *WeatherFetcher) Run(interval time.Duration, output chan<- string) {
	ticker := time.NewTicker(interval)
	defer ticker.Stop()

	output <- w.update()
	for range ticker.C {
		output <- w.update()
	}
}