Compare commits
6 Commits
Author | SHA1 | Date | |
---|---|---|---|
e93c7ec5c0 | |||
c2bde1e1dd | |||
44bf293eab | |||
f87b35b306 | |||
eaf2cf211d | |||
e8fbacfe6d |
4
TODO.md
4
TODO.md
@ -2,10 +2,12 @@
|
|||||||
- [ ] Helm Chart
|
- [ ] Helm Chart
|
||||||
- [ ] Update README
|
- [ ] Update README
|
||||||
- [ ] Add Grafana dashboard
|
- [ ] Add Grafana dashboard
|
||||||
- [ ] Add proxy to upstream support
|
|
||||||
- [ ] Add new spans
|
- [ ] Add new spans
|
||||||
|
|
||||||
## Done
|
## Done
|
||||||
|
- [x] Add proxy to upstream support
|
||||||
|
- [x] Fix wunderground 401
|
||||||
|
- [x] Perform proxy calls in goroutines
|
||||||
- [x] Configuration for app
|
- [x] Configuration for app
|
||||||
- [x] Configurable metric prefix
|
- [x] Configurable metric prefix
|
||||||
- [x] Add device name field with ID/Key mappings
|
- [x] Add device name field with ID/Key mappings
|
||||||
|
2
go.mod
2
go.mod
@ -3,7 +3,7 @@ module gitea.libretechconsulting.com/rmcguire/ambient-weather-local-exporter
|
|||||||
go 1.23.4
|
go 1.23.4
|
||||||
|
|
||||||
require (
|
require (
|
||||||
gitea.libretechconsulting.com/rmcguire/go-app v0.3.1
|
gitea.libretechconsulting.com/rmcguire/go-app v0.3.2
|
||||||
github.com/go-resty/resty/v2 v2.16.3
|
github.com/go-resty/resty/v2 v2.16.3
|
||||||
github.com/gorilla/schema v1.4.1
|
github.com/gorilla/schema v1.4.1
|
||||||
github.com/rs/zerolog v1.33.0
|
github.com/rs/zerolog v1.33.0
|
||||||
|
4
go.sum
4
go.sum
@ -1,5 +1,5 @@
|
|||||||
gitea.libretechconsulting.com/rmcguire/go-app v0.3.1 h1:jydcJ+Vv8sk+Le7nTI2+b6E7FfV+ShBJo7YdxmdaCYc=
|
gitea.libretechconsulting.com/rmcguire/go-app v0.3.2 h1:zAT6wmEEODYWx+iU3adlmFNW4FCTHzRvIepFIOQZGjk=
|
||||||
gitea.libretechconsulting.com/rmcguire/go-app v0.3.1/go.mod h1:wHOWh4O4AMDATQ3WEUYjq5a5bnICPBpu5G6BsNxqN38=
|
gitea.libretechconsulting.com/rmcguire/go-app v0.3.2/go.mod h1:ug6g+FyEi2LguWTQfd+bZrTd1ECsot8BylxgMFEO5DM=
|
||||||
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
|
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
|
||||||
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
|
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
|
||||||
github.com/caarlos0/env/v11 v11.3.1 h1:cArPWC15hWmEt+gWk7YBi7lEXTXCvpaSdCiZE2X5mCA=
|
github.com/caarlos0/env/v11 v11.3.1 h1:cArPWC15hWmEt+gWk7YBi7lEXTXCvpaSdCiZE2X5mCA=
|
||||||
|
@ -7,6 +7,7 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"sync"
|
||||||
|
|
||||||
"gitea.libretechconsulting.com/rmcguire/go-app/pkg/otel"
|
"gitea.libretechconsulting.com/rmcguire/go-app/pkg/otel"
|
||||||
"github.com/rs/zerolog"
|
"github.com/rs/zerolog"
|
||||||
@ -120,18 +121,40 @@ func (aw *AmbientWeather) handleProviderRequest(
|
|||||||
// Uses a weather update to allow awn to publish to wunderground and
|
// Uses a weather update to allow awn to publish to wunderground and
|
||||||
// visa versa.
|
// visa versa.
|
||||||
if station := update.StationConfig; station != nil {
|
if station := update.StationConfig; station != nil {
|
||||||
|
// Perform proxy updates in parallel if enabled
|
||||||
|
var proxyWg sync.WaitGroup
|
||||||
|
|
||||||
if station.ProxyToAWN {
|
if station.ProxyToAWN {
|
||||||
err := aw.awnProvider.ProxyReq(ctx, update)
|
proxyWg.Add(1)
|
||||||
if err != nil {
|
go func() {
|
||||||
zerolog.Ctx(aw.appCtx).Err(err).Msg("failed to proxy to ambient weather")
|
defer proxyWg.Done()
|
||||||
}
|
err := aw.awnProvider.ProxyReq(ctx, update)
|
||||||
|
if err != nil {
|
||||||
|
zerolog.Ctx(aw.appCtx).Err(err).Msg("failed to proxy to ambient weather")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
zerolog.Ctx(aw.appCtx).Debug().
|
||||||
|
Str("station", station.Name).
|
||||||
|
Msg("proxied weather update to awn")
|
||||||
|
}()
|
||||||
}
|
}
|
||||||
|
|
||||||
if station.ProxyToWunderground {
|
if station.ProxyToWunderground {
|
||||||
err := aw.wuProvider.ProxyReq(ctx, update)
|
proxyWg.Add(1)
|
||||||
if err != nil {
|
go func() {
|
||||||
zerolog.Ctx(aw.appCtx).Err(err).Msg("failed to proxy to ambient weather")
|
defer proxyWg.Done()
|
||||||
}
|
err := aw.wuProvider.ProxyReq(ctx, update)
|
||||||
|
if err != nil {
|
||||||
|
zerolog.Ctx(aw.appCtx).Err(err).Msg("failed to proxy to ambient weather")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
zerolog.Ctx(aw.appCtx).Debug().
|
||||||
|
Str("station", station.Name).
|
||||||
|
Msg("proxied weather update to wunderground")
|
||||||
|
}()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
proxyWg.Wait()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -18,6 +18,14 @@ const (
|
|||||||
awnURL = "http://ambientweather.net/data/report"
|
awnURL = "http://ambientweather.net/data/report"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Battery Sensors
|
||||||
|
const (
|
||||||
|
BattOutdoorSensor = "OutdoorSensor"
|
||||||
|
BattIndoorSensor = "IndoorSensor"
|
||||||
|
BattRainSensor = "RainSensor"
|
||||||
|
BattCO2Sensor = "CO2Sensor"
|
||||||
|
)
|
||||||
|
|
||||||
func (awn *AWNProvider) Name() string {
|
func (awn *AWNProvider) Name() string {
|
||||||
return providerName
|
return providerName
|
||||||
}
|
}
|
||||||
@ -66,19 +74,19 @@ func MapAwnUpdate(awnUpdate *AmbientWeatherUpdate) *weather.WeatherUpdate {
|
|||||||
TotalRainIn: awnUpdate.TotalRainIn,
|
TotalRainIn: awnUpdate.TotalRainIn,
|
||||||
Batteries: []weather.BatteryStatus{
|
Batteries: []weather.BatteryStatus{
|
||||||
{
|
{
|
||||||
Component: "OutdoorSensor",
|
Component: BattOutdoorSensor,
|
||||||
Status: awnUpdate.BattOut,
|
Status: awnUpdate.BattOut,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Component: "IndoorSensor",
|
Component: BattIndoorSensor,
|
||||||
Status: awnUpdate.BattIn,
|
Status: awnUpdate.BattIn,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Component: "RainSensor",
|
Component: BattRainSensor,
|
||||||
Status: awnUpdate.BattRain,
|
Status: awnUpdate.BattRain,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Component: "CO2Sensor",
|
Component: BattCO2Sensor,
|
||||||
Status: awnUpdate.BattCO2,
|
Status: awnUpdate.BattCO2,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -8,6 +8,7 @@ import (
|
|||||||
|
|
||||||
"gitea.libretechconsulting.com/rmcguire/go-app/pkg/otel"
|
"gitea.libretechconsulting.com/rmcguire/go-app/pkg/otel"
|
||||||
"github.com/go-resty/resty/v2"
|
"github.com/go-resty/resty/v2"
|
||||||
|
"github.com/rs/zerolog/log"
|
||||||
"go.opentelemetry.io/otel/attribute"
|
"go.opentelemetry.io/otel/attribute"
|
||||||
"go.opentelemetry.io/otel/codes"
|
"go.opentelemetry.io/otel/codes"
|
||||||
|
|
||||||
@ -43,12 +44,18 @@ func (awn *AWNProvider) ProxyReq(ctx context.Context, update *weather.WeatherUpd
|
|||||||
Get(awnURL)
|
Get(awnURL)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
span.RecordError(err)
|
span.RecordError(err)
|
||||||
|
span.SetAttributes(
|
||||||
|
attribute.String("query", resp.Request.QueryParam.Encode()),
|
||||||
|
attribute.String("body", string(resp.Body())),
|
||||||
|
)
|
||||||
span.SetStatus(codes.Error, err.Error())
|
span.SetStatus(codes.Error, err.Error())
|
||||||
|
log.Err(err).Any("query", resp.Request.PathParams).
|
||||||
|
Int("statusCode", resp.StatusCode()).
|
||||||
|
Msg("awn proxy failed")
|
||||||
}
|
}
|
||||||
|
|
||||||
span.SetAttributes(
|
span.SetAttributes(
|
||||||
attribute.Int("statusCode", resp.StatusCode()),
|
attribute.Int("statusCode", resp.StatusCode()),
|
||||||
attribute.String("body", string(resp.Body())),
|
|
||||||
)
|
)
|
||||||
|
|
||||||
return err
|
return err
|
||||||
@ -79,5 +86,20 @@ func updateToAWNParams(update *weather.WeatherUpdate) *url.Values {
|
|||||||
weather.SetURLVal(params, "windgustmph", update.WindGustMPH)
|
weather.SetURLVal(params, "windgustmph", update.WindGustMPH)
|
||||||
weather.SetURLVal(params, "windspeedmph", update.WindSpeedMPH)
|
weather.SetURLVal(params, "windspeedmph", update.WindSpeedMPH)
|
||||||
weather.SetURLVal(params, "maxdailygust", update.MaxDailyGust)
|
weather.SetURLVal(params, "maxdailygust", update.MaxDailyGust)
|
||||||
|
|
||||||
|
// Batteries
|
||||||
|
for _, status := range update.Batteries {
|
||||||
|
switch status.Component {
|
||||||
|
case BattOutdoorSensor:
|
||||||
|
weather.SetURLVal(params, "battin", status.Status)
|
||||||
|
case BattIndoorSensor:
|
||||||
|
weather.SetURLVal(params, "battout", status.Status)
|
||||||
|
case BattRainSensor:
|
||||||
|
weather.SetURLVal(params, "battrain", status.Status)
|
||||||
|
case BattCO2Sensor:
|
||||||
|
weather.SetURLVal(params, "batt_co2", status.Status)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return params
|
return params
|
||||||
}
|
}
|
||||||
|
@ -15,7 +15,7 @@ type WUProvider struct{}
|
|||||||
|
|
||||||
const (
|
const (
|
||||||
providerName = "weatherunderground"
|
providerName = "weatherunderground"
|
||||||
wuURL = "http://rtupdate.wunderground.com/weatherstation/updateweatherstation.php"
|
wuURL = "https://rtupdate.wunderground.com/weatherstation/updateweatherstation.php"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (wu *WUProvider) Name() string {
|
func (wu *WUProvider) Name() string {
|
||||||
|
@ -4,9 +4,9 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
"net/url"
|
"net/url"
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/go-resty/resty/v2"
|
"github.com/go-resty/resty/v2"
|
||||||
|
"github.com/rs/zerolog/log"
|
||||||
"go.opentelemetry.io/otel/attribute"
|
"go.opentelemetry.io/otel/attribute"
|
||||||
"go.opentelemetry.io/otel/codes"
|
"go.opentelemetry.io/otel/codes"
|
||||||
"k8s.io/utils/ptr"
|
"k8s.io/utils/ptr"
|
||||||
@ -39,12 +39,19 @@ func (wu *WUProvider) ProxyReq(ctx context.Context, update *weather.WeatherUpdat
|
|||||||
Get(wuURL)
|
Get(wuURL)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
span.SetStatus(codes.Error, err.Error())
|
span.SetStatus(codes.Error, err.Error())
|
||||||
|
span.SetAttributes(
|
||||||
|
attribute.String("query", resp.Request.QueryParam.Encode()),
|
||||||
|
attribute.String("body", string(resp.Body())),
|
||||||
|
)
|
||||||
span.RecordError(err)
|
span.RecordError(err)
|
||||||
|
log.Err(err).
|
||||||
|
Int("statusCode", resp.StatusCode()).
|
||||||
|
Any("query", resp.Request.PathParams).
|
||||||
|
Msg("wunderground proxy failed")
|
||||||
}
|
}
|
||||||
|
|
||||||
span.SetAttributes(
|
span.SetAttributes(
|
||||||
attribute.Int("statusCode", resp.StatusCode()),
|
attribute.Int("statusCode", resp.StatusCode()),
|
||||||
attribute.String("body", string(resp.Body())),
|
|
||||||
)
|
)
|
||||||
|
|
||||||
return err
|
return err
|
||||||
@ -52,11 +59,11 @@ func (wu *WUProvider) ProxyReq(ctx context.Context, update *weather.WeatherUpdat
|
|||||||
|
|
||||||
func updateToWuParams(u *weather.WeatherUpdate) *url.Values {
|
func updateToWuParams(u *weather.WeatherUpdate) *url.Values {
|
||||||
params := &url.Values{}
|
params := &url.Values{}
|
||||||
params.Set("dateutc", time.Now().Format(time.DateTime))
|
|
||||||
weather.SetURLVal(params, "ID", &u.StationConfig.WundergroundID)
|
weather.SetURLVal(params, "ID", &u.StationConfig.WundergroundID)
|
||||||
weather.SetURLVal(params, "PASSWORD", &u.StationConfig.WundergroundPassword)
|
weather.SetURLVal(params, "PASSWORD", &u.StationConfig.WundergroundPassword)
|
||||||
|
params.Set("action", "updateraw")
|
||||||
|
params.Set("dateutc", "now")
|
||||||
weather.SetURLVal(params, "UV", u.UV)
|
weather.SetURLVal(params, "UV", u.UV)
|
||||||
weather.SetURLVal(params, "action", ptr.To("updateraw"))
|
|
||||||
weather.SetURLVal(params, "baromin", u.BaromRelativeIn)
|
weather.SetURLVal(params, "baromin", u.BaromRelativeIn)
|
||||||
weather.SetURLVal(params, "dailyrainin", u.DailyRainIn)
|
weather.SetURLVal(params, "dailyrainin", u.DailyRainIn)
|
||||||
weather.SetURLVal(params, "dewptf", u.DewPointF)
|
weather.SetURLVal(params, "dewptf", u.DewPointF)
|
||||||
|
Reference in New Issue
Block a user