first commit
This commit is contained in:
99
internal/providers/wunderground.go
Normal file
99
internal/providers/wunderground.go
Normal file
@@ -0,0 +1,99 @@
|
||||
package providers
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"time"
|
||||
)
|
||||
|
||||
// WU PWS upload protocol endpoint. :contentReference[oaicite:1]{index=1}
|
||||
const wuEndpoint = "https://rtupdate.wunderground.com/weatherstation/updateweatherstation.php"
|
||||
|
||||
type WundergroundClient struct {
|
||||
HTTP *http.Client
|
||||
}
|
||||
|
||||
type WUUpload struct {
|
||||
StationID string
|
||||
StationKey string
|
||||
|
||||
// Units required by WU:
|
||||
// tempf (F), wind mph, baromin (inHg), rain inches.
|
||||
TempC float64
|
||||
Humidity float64
|
||||
WindDirDeg float64
|
||||
WindAvgMS float64
|
||||
WindMaxMS float64
|
||||
UVI float64
|
||||
SolarWm2 *float64 // your payload is lux; leave nil unless you add W/m^2 later
|
||||
|
||||
RainLastHourMM float64
|
||||
DailyRainMM float64
|
||||
|
||||
DateUTC string // "now" recommended
|
||||
}
|
||||
|
||||
func (c *WundergroundClient) Upload(ctx context.Context, u WUUpload) (string, error) {
|
||||
if c.HTTP == nil {
|
||||
c.HTTP = &http.Client{Timeout: 10 * time.Second}
|
||||
}
|
||||
|
||||
q := url.Values{}
|
||||
q.Set("ID", u.StationID)
|
||||
q.Set("PASSWORD", u.StationKey)
|
||||
q.Set("action", "updateraw")
|
||||
q.Set("dateutc", u.DateUTC) // "now" ok :contentReference[oaicite:2]{index=2}
|
||||
|
||||
// Required-ish observation fields (only set what we have)
|
||||
q.Set("tempf", fmt.Sprintf("%.2f", cToF(u.TempC)))
|
||||
q.Set("humidity", fmt.Sprintf("%.0f", u.Humidity))
|
||||
|
||||
q.Set("winddir", fmt.Sprintf("%.0f", u.WindDirDeg))
|
||||
q.Set("windspeedmph", fmt.Sprintf("%.2f", msToMph(u.WindAvgMS)))
|
||||
q.Set("windgustmph", fmt.Sprintf("%.2f", msToMph(u.WindMaxMS)))
|
||||
|
||||
// Rain (inches)
|
||||
q.Set("rainin", fmt.Sprintf("%.4f", mmToIn(u.RainLastHourMM)))
|
||||
q.Set("dailyrainin", fmt.Sprintf("%.4f", mmToIn(u.DailyRainMM)))
|
||||
|
||||
// UV index
|
||||
q.Set("UV", fmt.Sprintf("%.2f", u.UVI))
|
||||
|
||||
// NOTE: your WS90 payload provides light in lux, not W/m^2.
|
||||
// WU expects solarradiation in W/m^2, so we omit it unless you add a conversion/actual sensor field.
|
||||
if u.SolarWm2 != nil {
|
||||
q.Set("solarradiation", fmt.Sprintf("%.2f", *u.SolarWm2))
|
||||
}
|
||||
|
||||
// Optional: a software identifier
|
||||
q.Set("softwaretype", "go-weatherstation-go")
|
||||
|
||||
reqURL := wuEndpoint + "?" + q.Encode()
|
||||
req, err := http.NewRequestWithContext(ctx, http.MethodGet, reqURL, nil)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
resp, err := c.HTTP.Do(req)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
// WU typically returns plain text like "success"
|
||||
if resp.StatusCode/100 != 2 {
|
||||
return "", fmt.Errorf("wunderground upload HTTP %d", resp.StatusCode)
|
||||
}
|
||||
|
||||
// Read small body
|
||||
buf := make([]byte, 2048)
|
||||
n, _ := resp.Body.Read(buf)
|
||||
return strconv.Quote(string(buf[:n])), nil
|
||||
}
|
||||
|
||||
func cToF(c float64) float64 { return (c * 9.0 / 5.0) + 32.0 }
|
||||
func msToMph(ms float64) float64 { return ms * 2.2369362920544 }
|
||||
func mmToIn(mm float64) float64 { return mm / 25.4 }
|
||||
Reference in New Issue
Block a user