continue working on weather mapping

This commit is contained in:
Ryan McGuire 2025-03-15 09:53:13 -04:00
parent 69fd27916b
commit 220cc818e7
5 changed files with 90 additions and 45 deletions

64
main.go
View File

@ -9,36 +9,54 @@ import (
grpcopts "gitea.libretechconsulting.com/rmcguire/go-app/pkg/srv/grpc/opts"
httpopts "gitea.libretechconsulting.com/rmcguire/go-app/pkg/srv/http/opts"
"golang.org/x/sys/unix"
"k8s.io/utils/ptr"
weatherpb "gitea.libretechconsulting.com/rmcguire/ambient-local-exporter/api/v1alpha1/weather"
"gitea.libretechconsulting.com/rmcguire/ambient-local-exporter/pkg/ambient"
"gitea.libretechconsulting.com/rmcguire/ambient-local-exporter/pkg/ambient/ambienthttp"
"gitea.libretechconsulting.com/rmcguire/ambient-local-exporter/pkg/ambient/config"
weathergrpc "gitea.libretechconsulting.com/rmcguire/ambient-local-exporter/pkg/weather/grpc"
"gitea.libretechconsulting.com/rmcguire/ambient-local-exporter/pkg/weather/state"
)
const defaultMetricPrefix = "weather"
const (
defaultMetricPrefix = "weather"
defaultUpdatesToKeep = 120
)
func main() {
ctx, cncl := signal.NotifyContext(context.Background(), os.Kill, os.Interrupt, unix.SIGTERM)
defer cncl()
// Config type for app, which implements go-app/config.AppConfig
awConfig := &config.AmbientLocalExporterConfig{
// Read config and environment, prepare an appCtx, and merge
// go-app config into ambient weather exporter app config
ctx, awConfig := app.MustLoadConfigInto(ctx, &config.AmbientLocalExporterConfig{
MetricPrefix: defaultMetricPrefix,
WeatherStations: make([]config.WeatherStation, 0),
}
})
// Read config and environment, set up logging, load up
// an appCtx, and prepare ambient weather local exporter
ctx, awConfig = app.MustLoadConfigInto(ctx, awConfig)
// Prepare the exporter
// Prepare the ambient exporter with our prepared config
// and set up logging, tracing, etc..
aw := ambient.New(ctx, awConfig).Init()
// Define and prepare the app
awApp := app.App{
// Load http and grpc routes, prepare the app
awApp := prepareApp(ctx, aw)
// Run app and wait
awApp.MustRun()
<-awApp.Done()
}
func prepareApp(ctx context.Context, aw *ambient.AmbientWeather) *app.App {
// Config updates / defaults
if aw.Config.UpdatesToKeep == nil || *aw.Config.UpdatesToKeep < 1 {
aw.Config.UpdatesToKeep = ptr.To(defaultUpdatesToKeep)
}
// Load ambient routes into app
awApp := &app.App{
AppContext: ctx,
// HTTP Endpoints for Ambient Weather Stations
HTTP: &httpopts.AppHTTP{
Funcs: []httpopts.HTTPFunc{
@ -51,8 +69,13 @@ func main() {
HandlerFunc: aw.GetAWNHandlerFunc(ctx),
},
},
// HTTP Listener that fixes broken requests generated by
// some versions of awn firmware
CustomListener: ambienthttp.NewAWNMutatingListener(ctx,
awConfig.HTTP.Listen), // Necessary to fix certain bad AWN firmware
aw.Config.HTTP.Listen), // Necessary to fix certain bad AWN firmware
// Health check funcs
HealthChecks: []httpopts.HealthCheckFunc{
// TODO: Implement
func(ctx context.Context) error {
@ -60,19 +83,22 @@ func main() {
},
},
},
// GRPC Service for retrieving current weather
// GRPC Service for retrieving weather
GRPC: &grpcopts.AppGRPC{
Services: []*grpcopts.GRPCService{
{
Name: "Weather Service",
Type: &weatherpb.AmbientLocalWeatherService_ServiceDesc,
Service: weathergrpc.GRPCWeather{},
Name: "Weather Service",
Type: &weatherpb.AmbientLocalWeatherService_ServiceDesc,
Service: weathergrpc.NewGRPCWeather(ctx, state.NewWeatherState(
&state.Opts{
Ctx: ctx,
KeepLast: *aw.Config.UpdatesToKeep,
})),
},
},
},
}
// Run and wait
awApp.MustRun()
<-awApp.Done()
return awApp
}

View File

@ -27,7 +27,7 @@ type AmbientWeather struct {
// when either "AmbientWeather" or "Wunderground" are selected
// in the "Custom" section of the AWNet app, or the web UI
// of an Ambient WeatherHub
config *config.AmbientLocalExporterConfig
Config *config.AmbientLocalExporterConfig
awnProvider provider.AmbientProvider
wuProvider provider.AmbientProvider
appCtx context.Context
@ -37,7 +37,7 @@ type AmbientWeather struct {
func New(appCtx context.Context, awConfig *config.AmbientLocalExporterConfig) *AmbientWeather {
return &AmbientWeather{
config: awConfig,
Config: awConfig,
appCtx: appCtx,
}
}
@ -48,7 +48,7 @@ func (aw *AmbientWeather) Init() *AmbientWeather {
aw.wuProvider = &wunderground.WUProvider{}
aw.l = zerolog.Ctx(aw.appCtx)
aw.l.Trace().Any("awConfig", aw.config).Send()
aw.l.Trace().Any("awConfig", aw.Config).Send()
return aw
}
@ -227,15 +227,15 @@ func (aw *AmbientWeather) proxyUpdate(
}
func (aw *AmbientWeather) InitMetrics() {
if aw.config.MetricPrefix != "" {
weather.MetricPrefix = aw.config.MetricPrefix
if aw.Config.MetricPrefix != "" {
weather.MetricPrefix = aw.Config.MetricPrefix
}
aw.metrics = weather.MustInitMetrics(aw.appCtx)
}
func (aw *AmbientWeather) enrichStation(update *weather.WeatherUpdate) {
if update != nil && update.StationID != nil && *update.StationID != "" {
for _, station := range aw.config.WeatherStations {
for _, station := range aw.Config.WeatherStations {
if *update.StationID == station.AWNPassKey || *update.StationID == station.WundergroundID {
update.StationConfig = &station
}

View File

@ -7,7 +7,8 @@ import (
// This configuration includes all config from go-app/config.AppConfig
type AmbientLocalExporterConfig struct {
MetricPrefix string `yaml:"metricPrefix" default:"weather" env:"AMBIENT_METRIC_PREFIX"`
WeatherStations []WeatherStation `yaml:"weatherStations"` // No env, too complex, not worth the time
UpdatesToKeep *int `yaml:"updatesToKeep" default:"1" env:"AMBIENT_UPDATES_TO_KEEP"`
WeatherStations []WeatherStation `yaml:"weatherStations" env:"weatherStations"` // No env, too complex, not worth the time
*config.AppConfig // Extends app config
}

View File

@ -25,25 +25,44 @@ func UpdateToPbUpdate(u *weather.WeatherUpdate) *pb.WeatherUpdate {
TempIndoorF: u.TempIndoorF,
HumidityOutdoor: ptr.To(int32(*u.HumidityOudoor)),
HumidityIndoor: ptr.To(int32(*u.HumidityIndoor)),
WindSpeedMph: new(float64),
WindGustMph: new(float64),
MaxDailyGust: new(float64),
WindDir: new(int32),
WindDirAvg_10M: new(int32),
Uv: new(int32),
SolarRadiation: new(float64),
HourlyRainIn: new(float64),
EventRainIn: new(float64),
DailyRainIn: new(float64),
WeeklyRainIn: new(float64),
MonthlyRainIn: new(float64),
YearlyRainIn: new(float64),
TotalRainIn: new(float64),
WindSpeedMph: ptr.To(*u.WindSpeedMPH),
WindGustMph: ptr.To(*u.WindGustMPH),
MaxDailyGust: ptr.To(*u.MaxDailyGust),
WindDir: ptr.To(int32(*u.WindDir)),
WindDirAvg_10M: ptr.To(int32(*u.WindDirAvg10m)),
Uv: ptr.To(int32(*u.UV)),
SolarRadiation: ptr.To(*u.SolarRadiation),
HourlyRainIn: ptr.To(*u.HourlyRainIn),
EventRainIn: ptr.To(*u.EventRainIn),
DailyRainIn: ptr.To(*u.DailyRainIn),
WeeklyRainIn: ptr.To(*u.WeeklyRainIn),
MonthlyRainIn: ptr.To(*u.MonthlyRainIn),
YearlyRainIn: ptr.To(*u.YearlyRainIn),
TotalRainIn: ptr.To(*u.TotalRainIn),
Batteries: []*pb.BatteryStatus{},
BaromRelativeIn: new(float64),
BaromAbsoluteIn: new(float64),
DewPointF: new(float64),
WindChillF: new(float64),
BaromRelativeIn: ptr.To(*u.BaromRelativeIn),
BaromAbsoluteIn: ptr.To(*u.BaromAbsoluteIn),
DewPointF: ptr.To(*u.DewPointF),
WindChillF: ptr.To(*u.WindChillF),
TempHumiditySensors: []*pb.TempHumiditySensor{},
}
}
func BatteriesToPbBatteries(batteries []weather.BatteryStatus) []*pb.BatteryStatus {
pbBatteries := make([]*pb.BatteryStatus, len(batteries))
for i, b := range batteries {
pbBatteries[i] = &pb.BatteryStatus{
Component: b.Component,
Status: ptr.To(int32(*b.Status)),
}
}
return pbBatteries
}
func nilOrValPtr[T *int32 | *float64](v T) *T {
switch T.(type) {
case *int32:
return v.(*int32)
}
return nil
}

View File

@ -16,7 +16,6 @@ import (
"gitea.libretechconsulting.com/rmcguire/ambient-local-exporter/pkg/weather/state"
)
// TODO: Implement
type GRPCWeather struct {
ctx context.Context
state *state.WeatherState