182 lines
5.7 KiB
Go
182 lines
5.7 KiB
Go
package awn
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"net/http"
|
|
"net/url"
|
|
"time"
|
|
|
|
"github.com/go-resty/resty/v2"
|
|
"github.com/gorilla/schema"
|
|
"go.opentelemetry.io/otel/attribute"
|
|
"go.opentelemetry.io/otel/codes"
|
|
|
|
"gitea.libretechconsulting.com/rmcguire/go-app/pkg/otel"
|
|
|
|
"gitea.libretechconsulting.com/rmcguire/ambient-weather-local-exporter/pkg/weather"
|
|
)
|
|
|
|
type AWNProvider struct{}
|
|
|
|
const (
|
|
providerName = "awn"
|
|
awnURL = "http://ambientweather.net/data/report"
|
|
)
|
|
|
|
func (awn *AWNProvider) Name() string {
|
|
return providerName
|
|
}
|
|
|
|
// Takes an inbound request from the ambient device and maps
|
|
// to a stable struct for weather updates
|
|
func (awn *AWNProvider) ReqToWeather(_ context.Context, r *http.Request) (
|
|
*weather.WeatherUpdate, error,
|
|
) {
|
|
awnUpdate, err := UnmarshalQueryParams(r.URL.Query())
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return MapAwnUpdate(awnUpdate), nil
|
|
}
|
|
|
|
// Attempts to proxy the weather station update to awn
|
|
// SAMPLE:
|
|
// {"PASSKEY":["ABF7E052BC7325A32300ACC89112AA91"],"baromabsin":["28.895"],
|
|
// "baromrelin":["29.876"],"battin":["1"],"battout":["1"],"battrain":["1"],
|
|
// "dailyrainin":["0.000"],"dateutc":["2025-01-11 22:07:57"],"eventrainin":["0.000"],
|
|
// "hourlyrainin":["0.000"],"humidity":["76"],"humidityin":["31"],"maxdailygust":["7.83"],
|
|
// "monthlyrainin":["0.000"],"solarradiation":["14.21"],"stationtype":["WeatherHub_V1.0.1"],
|
|
// "tempf":["29.48"],"tempinf":["66.20"],"totalrainin":["0.000"],"uv":["0"],
|
|
// "weeklyrainin":["0.000"],"winddir":["66"],"winddir_avg10m":["268"],"windgustmph":["2.68"],
|
|
// "windspeedmph":["0.00"],"yearlyrainin":["0.000"]}
|
|
func (awn *AWNProvider) ProxyReq(ctx context.Context, update *weather.WeatherUpdate) error {
|
|
tracer := otel.GetTracer(ctx, "awnProvider", "proxyReq")
|
|
ctx, span := tracer.Start(ctx, "proxyToAWN")
|
|
defer span.End()
|
|
|
|
if update.WeatherServiceCredentials["PASSKEY"] == "" {
|
|
err := errors.New("no PASSKEY set in update")
|
|
span.RecordError(err)
|
|
return err
|
|
}
|
|
|
|
params := updateToAWNParams(update)
|
|
|
|
resp, err := resty.New().R().
|
|
SetContext(ctx).
|
|
SetQueryParamsFromValues(*params).
|
|
Get(awnURL)
|
|
if err != nil {
|
|
span.RecordError(err)
|
|
span.SetStatus(codes.Error, err.Error())
|
|
}
|
|
|
|
span.SetAttributes(
|
|
attribute.Int("statusCode", resp.StatusCode()),
|
|
attribute.String("body", string(resp.Body())),
|
|
)
|
|
|
|
return err
|
|
}
|
|
|
|
func updateToAWNParams(update *weather.WeatherUpdate) *url.Values {
|
|
params := &url.Values{}
|
|
params.Set("PASSKEY", update.WeatherServiceCredentials["PASSKEY"])
|
|
params.Set("dateutc", time.Now().Format(time.DateTime))
|
|
weather.SetURLVal(params, "baromabsin", update.BaromAbsoluteIn)
|
|
weather.SetURLVal(params, "baromrelin", update.BaromRelativeIn)
|
|
weather.SetURLVal(params, "dailyrainin", update.DailyRainIn)
|
|
weather.SetURLVal(params, "weeklyrainin", update.WeeklyRainIn)
|
|
weather.SetURLVal(params, "eventrainin", update.EventRainIn)
|
|
weather.SetURLVal(params, "hourlyrainin", update.HourlyRainIn)
|
|
weather.SetURLVal(params, "monthlyrainin", update.MonthlyRainIn)
|
|
weather.SetURLVal(params, "yearlyrainin", update.YearlyRainIn)
|
|
weather.SetURLVal(params, "totalrainin", update.TotalRainIn)
|
|
weather.SetURLVal(params, "humidity", update.HumidityOudoor)
|
|
weather.SetURLVal(params, "humidityin", update.HumidityIndoor)
|
|
weather.SetURLVal(params, "solarradiation", update.SolarRadiation)
|
|
weather.SetURLVal(params, "uv", update.UV)
|
|
weather.SetURLVal(params, "stationtype", update.StationType)
|
|
weather.SetURLVal(params, "tempf", update.TempOutdoorF)
|
|
weather.SetURLVal(params, "tempinf", update.TempIndoorF)
|
|
weather.SetURLVal(params, "winddir", update.WindDir)
|
|
weather.SetURLVal(params, "winddir_avg10m", update.WindDirAvg10m)
|
|
weather.SetURLVal(params, "windgustmph", update.WindGustMPH)
|
|
weather.SetURLVal(params, "windspeedmph", update.WindSpeedMPH)
|
|
weather.SetURLVal(params, "maxdailygust", update.MaxDailyGust)
|
|
return params
|
|
}
|
|
|
|
func MapAwnUpdate(awnUpdate *AmbientWeatherUpdate) *weather.WeatherUpdate {
|
|
updateTime := time.Now()
|
|
if awnUpdate.DateUTC != nil {
|
|
ut, err := time.Parse(time.DateTime, *awnUpdate.DateUTC)
|
|
if err == nil {
|
|
updateTime = ut
|
|
}
|
|
}
|
|
|
|
credentials := make(map[string]string)
|
|
if awnUpdate.PassKey != nil {
|
|
credentials["PASSKEY"] = *awnUpdate.PassKey
|
|
}
|
|
|
|
return &weather.WeatherUpdate{
|
|
DateUTC: &updateTime,
|
|
StationID: awnUpdate.PassKey,
|
|
StationType: awnUpdate.StationType,
|
|
TempOutdoorF: awnUpdate.TempF,
|
|
HumidityOudoor: awnUpdate.Humidity,
|
|
WindSpeedMPH: awnUpdate.WindSpeedMPH,
|
|
WindGustMPH: awnUpdate.WindGustMPH,
|
|
MaxDailyGust: awnUpdate.MaxDailyGust,
|
|
WindDir: awnUpdate.WindDir,
|
|
WindDirAvg10m: awnUpdate.WindDirAVG10m,
|
|
UV: awnUpdate.UV,
|
|
SolarRadiation: awnUpdate.SolarRadiation,
|
|
HourlyRainIn: awnUpdate.HourlyRainIn,
|
|
EventRainIn: awnUpdate.EventRainIn,
|
|
DailyRainIn: awnUpdate.DailyRainIn,
|
|
WeeklyRainIn: awnUpdate.WeeklyRainIn,
|
|
MonthlyRainIn: awnUpdate.MonthlyRainIn,
|
|
YearlyRainIn: awnUpdate.YearlyRainIn,
|
|
TotalRainIn: awnUpdate.TotalRainIn,
|
|
Batteries: []weather.BatteryStatus{
|
|
{
|
|
Component: "OutdoorSensor",
|
|
Status: awnUpdate.BattOut,
|
|
},
|
|
{
|
|
Component: "IndoorSensor",
|
|
Status: awnUpdate.BattIn,
|
|
},
|
|
{
|
|
Component: "RainSensor",
|
|
Status: awnUpdate.BattRain,
|
|
},
|
|
{
|
|
Component: "CO2Sensor",
|
|
Status: awnUpdate.BattCO2,
|
|
},
|
|
},
|
|
TempIndoorF: awnUpdate.TempInF,
|
|
HumidityIndoor: awnUpdate.HumidityIn,
|
|
BaromRelativeIn: awnUpdate.BaromRelIn,
|
|
BaromAbsoluteIn: awnUpdate.BaromAbsIn,
|
|
WeatherServiceCredentials: credentials,
|
|
}
|
|
}
|
|
|
|
func UnmarshalQueryParams(query url.Values) (*AmbientWeatherUpdate, error) {
|
|
update := new(AmbientWeatherUpdate)
|
|
|
|
decoder := schema.NewDecoder()
|
|
if err := decoder.Decode(update, query); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return update, nil
|
|
}
|