100 lines
2.6 KiB
Go
100 lines
2.6 KiB
Go
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 }
|