Compare commits
	
		
			27 Commits
		
	
	
		
			v0.5.5
			...
			7e53afe035
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 7e53afe035 | |||
| be2895695f | |||
| 27af90322a | |||
| a8e7b24656 | |||
| 2653825ac8 | |||
| a99e52ae4c | |||
| 48828bf8d0 | |||
| bcf4c0b5ce | |||
| 8674bb4e01 | |||
| 74120183ab | |||
| f05496632a | |||
| ffac524cfc | |||
| 64ca321c3a | |||
| 3329c980a9 | |||
| b4412a461f | |||
| 042fd221c4 | |||
| 29433cddd7 | |||
| f98a4cf348 | |||
| 8b46238e49 | |||
| 4ed1e465d2 | |||
| ea93beb6b2 | |||
| ae53a1d5fd | |||
| cd04beeec6 | |||
| 3d3492a283 | |||
| ce0ef7d291 | |||
| f2f160b112 | |||
| 2c29493229 | 
							
								
								
									
										4
									
								
								.dockerignore
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								.dockerignore
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,4 @@
 | 
			
		||||
.git
 | 
			
		||||
config.y*ml
 | 
			
		||||
go.work*
 | 
			
		||||
docker-compose-sample*
 | 
			
		||||
@@ -16,7 +16,7 @@ env:
 | 
			
		||||
  DOCKER_USER: rmcguire
 | 
			
		||||
  DOCKER_REPO: rmcguire/ambient-local-exporter
 | 
			
		||||
  DOCKER_IMG: ${{ env.DOCKER_REGISTRY }}/${{ env.DOCKER_REPO }}
 | 
			
		||||
 | 
			
		||||
  CHART_DIR: helm/ambient-local-exporter
 | 
			
		||||
 | 
			
		||||
jobs:
 | 
			
		||||
  release:
 | 
			
		||||
@@ -54,6 +54,7 @@ jobs:
 | 
			
		||||
          done
 | 
			
		||||
 | 
			
		||||
      - name: Run Go List
 | 
			
		||||
        continue-on-error: true
 | 
			
		||||
        env:
 | 
			
		||||
          TAG_NAME: ${{ github.ref_name }} # Use the pushed tag name
 | 
			
		||||
        run: |
 | 
			
		||||
@@ -85,3 +86,28 @@ jobs:
 | 
			
		||||
          build-args: |
 | 
			
		||||
            VER_PKG=${{ env.VER_PKG }}
 | 
			
		||||
            VERSION=${{ github.ref_name }}
 | 
			
		||||
 | 
			
		||||
  helm-release:
 | 
			
		||||
    runs-on: ubuntu-latest
 | 
			
		||||
    steps:
 | 
			
		||||
      - name: Checkout Code
 | 
			
		||||
        uses: actions/checkout@v3
 | 
			
		||||
 | 
			
		||||
      - name: Install Helm
 | 
			
		||||
        env:
 | 
			
		||||
          BINARY_NAME: helm
 | 
			
		||||
        run: |
 | 
			
		||||
          curl -fsSL https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 | bash
 | 
			
		||||
 | 
			
		||||
      - name: Package Chart
 | 
			
		||||
        run: |
 | 
			
		||||
          helm package --app-version ${VERSION#v} ${CHART_DIR}
 | 
			
		||||
 | 
			
		||||
      - name: Publish Chart
 | 
			
		||||
        env:
 | 
			
		||||
          API_TOKEN: ${{ secrets.API_TOKEN }}
 | 
			
		||||
        run: |
 | 
			
		||||
          curl -X POST \
 | 
			
		||||
            -H "Authorization: token ${API_TOKEN}" \
 | 
			
		||||
            --upload-file ./${PACKAGE_NAME}-*.tgz \
 | 
			
		||||
            https://gitea.libretechconsulting.com/api/packages/${GITHUB_REPOSITORY_OWNER}/helm/api/charts
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										6
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										6
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							@@ -26,5 +26,9 @@ go.work.sum
 | 
			
		||||
 | 
			
		||||
bin/*
 | 
			
		||||
 | 
			
		||||
config.yaml
 | 
			
		||||
config.y*ml
 | 
			
		||||
docker-compose.yml
 | 
			
		||||
 | 
			
		||||
.vscode
 | 
			
		||||
helm/values*.y*ml
 | 
			
		||||
helm_upgrade.sh
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										23
									
								
								Makefile
									
									
									
									
									
								
							
							
						
						
									
										23
									
								
								Makefile
									
									
									
									
									
								
							@@ -1,14 +1,23 @@
 | 
			
		||||
CMD_NAME := ambient-local-exporter
 | 
			
		||||
 | 
			
		||||
.PHONY: all test build docker install clean
 | 
			
		||||
.PHONY: all test build docker install clean proto check_protoc
 | 
			
		||||
 | 
			
		||||
VERSION ?= development # Default to "development" if VERSION is not set
 | 
			
		||||
APIVERSION := v1alpha1
 | 
			
		||||
API_DIR := api/$(APIVERSION)
 | 
			
		||||
PROTO_DIRS := $(wildcard proto/*)
 | 
			
		||||
PLATFORMS := linux/amd64 linux/arm64 darwin/amd64 darwin/arm64
 | 
			
		||||
OUTPUT_DIR := bin
 | 
			
		||||
VER_PKG := gitea.libretechconsulting.com/rmcguire/go-app/pkg/config.Version
 | 
			
		||||
DOCKER_IMG := gitea.libretechconsulting.com/rmcguire/ambient-local-exporter
 | 
			
		||||
 | 
			
		||||
all: test build docker
 | 
			
		||||
all: proto test build docker
 | 
			
		||||
 | 
			
		||||
proto: check_protoc $(API_DIR)
 | 
			
		||||
	protoc --proto_path=proto \
 | 
			
		||||
		--go_out=$(API_DIR) --go_opt=paths=source_relative \
 | 
			
		||||
		--go-grpc_out=$(API_DIR) --go-grpc_opt=paths=source_relative \
 | 
			
		||||
		$(foreach dir, $(PROTO_DIRS), $(wildcard $(dir)/*.proto))
 | 
			
		||||
 | 
			
		||||
test:
 | 
			
		||||
	go test -v ./...
 | 
			
		||||
@@ -37,3 +46,13 @@ install:
 | 
			
		||||
 | 
			
		||||
clean:
 | 
			
		||||
	rm -rf bin/${CMD_NAME}
 | 
			
		||||
 | 
			
		||||
check_protoc:
 | 
			
		||||
	@if ! command -v protoc-gen-go > /dev/null; then \
 | 
			
		||||
		echo "Error: protoc-gen-go not found in PATH"; \
 | 
			
		||||
		exit 1; \
 | 
			
		||||
	fi
 | 
			
		||||
	@if ! command -v protoc-gen-go-grpc > /dev/null; then \
 | 
			
		||||
		echo "Error: protoc-gen-go-grpc not found in PATH"; \
 | 
			
		||||
		exit 1; \
 | 
			
		||||
	fi
 | 
			
		||||
 
 | 
			
		||||
@@ -1,3 +1,3 @@
 | 
			
		||||
# ambient-weather-local-exporter
 | 
			
		||||
# ambient-local-exporter
 | 
			
		||||
 | 
			
		||||
A simple exporter that takes data from an Ambient Weather weather station or Weather Hub. Requires no internet connection, and does not use any cloud APIs.
 | 
			
		||||
							
								
								
									
										1
									
								
								TODO.md
									
									
									
									
									
								
							
							
						
						
									
										1
									
								
								TODO.md
									
									
									
									
									
								
							@@ -1,4 +1,5 @@
 | 
			
		||||
# TODO
 | 
			
		||||
- [ ] Finish implementing weather GRPC
 | 
			
		||||
- [ ] Helm Chart
 | 
			
		||||
- [ ] Update README
 | 
			
		||||
- [ ] Add Grafana dashboard
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										752
									
								
								api/v1alpha1/weather/weather.pb.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										752
									
								
								api/v1alpha1/weather/weather.pb.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,752 @@
 | 
			
		||||
// Code generated by protoc-gen-go. DO NOT EDIT.
 | 
			
		||||
// versions:
 | 
			
		||||
// 	protoc-gen-go v1.36.5
 | 
			
		||||
// 	protoc        v5.29.3
 | 
			
		||||
// source: weather/weather.proto
 | 
			
		||||
 | 
			
		||||
package weather
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	protoreflect "google.golang.org/protobuf/reflect/protoreflect"
 | 
			
		||||
	protoimpl "google.golang.org/protobuf/runtime/protoimpl"
 | 
			
		||||
	timestamppb "google.golang.org/protobuf/types/known/timestamppb"
 | 
			
		||||
	reflect "reflect"
 | 
			
		||||
	sync "sync"
 | 
			
		||||
	unsafe "unsafe"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	// Verify that this generated code is sufficiently up-to-date.
 | 
			
		||||
	_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
 | 
			
		||||
	// Verify that runtime/protoimpl is sufficiently up-to-date.
 | 
			
		||||
	_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type GetWeatherRequest struct {
 | 
			
		||||
	state         protoimpl.MessageState `protogen:"open.v1"`
 | 
			
		||||
	Opts          *GetWeatherOpts        `protobuf:"bytes,1,opt,name=opts,proto3" json:"opts,omitempty"`
 | 
			
		||||
	unknownFields protoimpl.UnknownFields
 | 
			
		||||
	sizeCache     protoimpl.SizeCache
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (x *GetWeatherRequest) Reset() {
 | 
			
		||||
	*x = GetWeatherRequest{}
 | 
			
		||||
	mi := &file_weather_weather_proto_msgTypes[0]
 | 
			
		||||
	ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 | 
			
		||||
	ms.StoreMessageInfo(mi)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (x *GetWeatherRequest) String() string {
 | 
			
		||||
	return protoimpl.X.MessageStringOf(x)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (*GetWeatherRequest) ProtoMessage() {}
 | 
			
		||||
 | 
			
		||||
func (x *GetWeatherRequest) ProtoReflect() protoreflect.Message {
 | 
			
		||||
	mi := &file_weather_weather_proto_msgTypes[0]
 | 
			
		||||
	if x != nil {
 | 
			
		||||
		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 | 
			
		||||
		if ms.LoadMessageInfo() == nil {
 | 
			
		||||
			ms.StoreMessageInfo(mi)
 | 
			
		||||
		}
 | 
			
		||||
		return ms
 | 
			
		||||
	}
 | 
			
		||||
	return mi.MessageOf(x)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Deprecated: Use GetWeatherRequest.ProtoReflect.Descriptor instead.
 | 
			
		||||
func (*GetWeatherRequest) Descriptor() ([]byte, []int) {
 | 
			
		||||
	return file_weather_weather_proto_rawDescGZIP(), []int{0}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (x *GetWeatherRequest) GetOpts() *GetWeatherOpts {
 | 
			
		||||
	if x != nil {
 | 
			
		||||
		return x.Opts
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type GetWeatherResponse struct {
 | 
			
		||||
	state          protoimpl.MessageState `protogen:"open.v1"`
 | 
			
		||||
	LastUpdated    *timestamppb.Timestamp `protobuf:"bytes,1,opt,name=last_updated,json=lastUpdated,proto3" json:"last_updated,omitempty"`
 | 
			
		||||
	WeatherUpdates []*WeatherUpdate       `protobuf:"bytes,2,rep,name=weather_updates,json=weatherUpdates,proto3" json:"weather_updates,omitempty"`
 | 
			
		||||
	unknownFields  protoimpl.UnknownFields
 | 
			
		||||
	sizeCache      protoimpl.SizeCache
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (x *GetWeatherResponse) Reset() {
 | 
			
		||||
	*x = GetWeatherResponse{}
 | 
			
		||||
	mi := &file_weather_weather_proto_msgTypes[1]
 | 
			
		||||
	ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 | 
			
		||||
	ms.StoreMessageInfo(mi)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (x *GetWeatherResponse) String() string {
 | 
			
		||||
	return protoimpl.X.MessageStringOf(x)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (*GetWeatherResponse) ProtoMessage() {}
 | 
			
		||||
 | 
			
		||||
func (x *GetWeatherResponse) ProtoReflect() protoreflect.Message {
 | 
			
		||||
	mi := &file_weather_weather_proto_msgTypes[1]
 | 
			
		||||
	if x != nil {
 | 
			
		||||
		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 | 
			
		||||
		if ms.LoadMessageInfo() == nil {
 | 
			
		||||
			ms.StoreMessageInfo(mi)
 | 
			
		||||
		}
 | 
			
		||||
		return ms
 | 
			
		||||
	}
 | 
			
		||||
	return mi.MessageOf(x)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Deprecated: Use GetWeatherResponse.ProtoReflect.Descriptor instead.
 | 
			
		||||
func (*GetWeatherResponse) Descriptor() ([]byte, []int) {
 | 
			
		||||
	return file_weather_weather_proto_rawDescGZIP(), []int{1}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (x *GetWeatherResponse) GetLastUpdated() *timestamppb.Timestamp {
 | 
			
		||||
	if x != nil {
 | 
			
		||||
		return x.LastUpdated
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (x *GetWeatherResponse) GetWeatherUpdates() []*WeatherUpdate {
 | 
			
		||||
	if x != nil {
 | 
			
		||||
		return x.WeatherUpdates
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type GetWeatherOpts struct {
 | 
			
		||||
	state         protoimpl.MessageState `protogen:"open.v1"`
 | 
			
		||||
	StationName   *string                `protobuf:"bytes,1,opt,name=station_name,json=stationName,proto3,oneof" json:"station_name,omitempty"`
 | 
			
		||||
	StationType   *string                `protobuf:"bytes,2,opt,name=station_type,json=stationType,proto3,oneof" json:"station_type,omitempty"`
 | 
			
		||||
	unknownFields protoimpl.UnknownFields
 | 
			
		||||
	sizeCache     protoimpl.SizeCache
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (x *GetWeatherOpts) Reset() {
 | 
			
		||||
	*x = GetWeatherOpts{}
 | 
			
		||||
	mi := &file_weather_weather_proto_msgTypes[2]
 | 
			
		||||
	ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 | 
			
		||||
	ms.StoreMessageInfo(mi)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (x *GetWeatherOpts) String() string {
 | 
			
		||||
	return protoimpl.X.MessageStringOf(x)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (*GetWeatherOpts) ProtoMessage() {}
 | 
			
		||||
 | 
			
		||||
func (x *GetWeatherOpts) ProtoReflect() protoreflect.Message {
 | 
			
		||||
	mi := &file_weather_weather_proto_msgTypes[2]
 | 
			
		||||
	if x != nil {
 | 
			
		||||
		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 | 
			
		||||
		if ms.LoadMessageInfo() == nil {
 | 
			
		||||
			ms.StoreMessageInfo(mi)
 | 
			
		||||
		}
 | 
			
		||||
		return ms
 | 
			
		||||
	}
 | 
			
		||||
	return mi.MessageOf(x)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Deprecated: Use GetWeatherOpts.ProtoReflect.Descriptor instead.
 | 
			
		||||
func (*GetWeatherOpts) Descriptor() ([]byte, []int) {
 | 
			
		||||
	return file_weather_weather_proto_rawDescGZIP(), []int{2}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (x *GetWeatherOpts) GetStationName() string {
 | 
			
		||||
	if x != nil && x.StationName != nil {
 | 
			
		||||
		return *x.StationName
 | 
			
		||||
	}
 | 
			
		||||
	return ""
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (x *GetWeatherOpts) GetStationType() string {
 | 
			
		||||
	if x != nil && x.StationType != nil {
 | 
			
		||||
		return *x.StationType
 | 
			
		||||
	}
 | 
			
		||||
	return ""
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type WeatherUpdate struct {
 | 
			
		||||
	state               protoimpl.MessageState `protogen:"open.v1"`
 | 
			
		||||
	StationName         string                 `protobuf:"bytes,1,opt,name=station_name,json=stationName,proto3" json:"station_name,omitempty"`
 | 
			
		||||
	StationType         string                 `protobuf:"bytes,2,opt,name=station_type,json=stationType,proto3" json:"station_type,omitempty"`
 | 
			
		||||
	StationId           string                 `protobuf:"bytes,3,opt,name=station_id,json=stationId,proto3" json:"station_id,omitempty"`
 | 
			
		||||
	TempOutdoorF        *float64               `protobuf:"fixed64,4,opt,name=temp_outdoor_f,json=tempOutdoorF,proto3,oneof" json:"temp_outdoor_f,omitempty"`
 | 
			
		||||
	TempIndoorF         *float64               `protobuf:"fixed64,5,opt,name=temp_indoor_f,json=tempIndoorF,proto3,oneof" json:"temp_indoor_f,omitempty"`
 | 
			
		||||
	HumidityOutdoor     *int32                 `protobuf:"varint,6,opt,name=humidity_outdoor,json=humidityOutdoor,proto3,oneof" json:"humidity_outdoor,omitempty"`
 | 
			
		||||
	HumidityIndoor      *int32                 `protobuf:"varint,7,opt,name=humidity_indoor,json=humidityIndoor,proto3,oneof" json:"humidity_indoor,omitempty"`
 | 
			
		||||
	WindSpeedMph        *float64               `protobuf:"fixed64,8,opt,name=wind_speed_mph,json=windSpeedMph,proto3,oneof" json:"wind_speed_mph,omitempty"`
 | 
			
		||||
	WindGustMph         *float64               `protobuf:"fixed64,9,opt,name=wind_gust_mph,json=windGustMph,proto3,oneof" json:"wind_gust_mph,omitempty"`
 | 
			
		||||
	MaxDailyGust        *float64               `protobuf:"fixed64,10,opt,name=max_daily_gust,json=maxDailyGust,proto3,oneof" json:"max_daily_gust,omitempty"`
 | 
			
		||||
	WindDir             *int32                 `protobuf:"varint,11,opt,name=wind_dir,json=windDir,proto3,oneof" json:"wind_dir,omitempty"`
 | 
			
		||||
	WindDirAvg_10M      *int32                 `protobuf:"varint,12,opt,name=wind_dir_avg_10m,json=windDirAvg10m,proto3,oneof" json:"wind_dir_avg_10m,omitempty"`
 | 
			
		||||
	Uv                  *int32                 `protobuf:"varint,13,opt,name=uv,proto3,oneof" json:"uv,omitempty"`
 | 
			
		||||
	SolarRadiation      *float64               `protobuf:"fixed64,14,opt,name=solar_radiation,json=solarRadiation,proto3,oneof" json:"solar_radiation,omitempty"`
 | 
			
		||||
	HourlyRainIn        *float64               `protobuf:"fixed64,15,opt,name=hourly_rain_in,json=hourlyRainIn,proto3,oneof" json:"hourly_rain_in,omitempty"`
 | 
			
		||||
	EventRainIn         *float64               `protobuf:"fixed64,16,opt,name=event_rain_in,json=eventRainIn,proto3,oneof" json:"event_rain_in,omitempty"`
 | 
			
		||||
	DailyRainIn         *float64               `protobuf:"fixed64,17,opt,name=daily_rain_in,json=dailyRainIn,proto3,oneof" json:"daily_rain_in,omitempty"`
 | 
			
		||||
	WeeklyRainIn        *float64               `protobuf:"fixed64,18,opt,name=weekly_rain_in,json=weeklyRainIn,proto3,oneof" json:"weekly_rain_in,omitempty"`
 | 
			
		||||
	MonthlyRainIn       *float64               `protobuf:"fixed64,19,opt,name=monthly_rain_in,json=monthlyRainIn,proto3,oneof" json:"monthly_rain_in,omitempty"`
 | 
			
		||||
	YearlyRainIn        *float64               `protobuf:"fixed64,20,opt,name=yearly_rain_in,json=yearlyRainIn,proto3,oneof" json:"yearly_rain_in,omitempty"`
 | 
			
		||||
	TotalRainIn         *float64               `protobuf:"fixed64,21,opt,name=total_rain_in,json=totalRainIn,proto3,oneof" json:"total_rain_in,omitempty"`
 | 
			
		||||
	Batteries           []*BatteryStatus       `protobuf:"bytes,22,rep,name=batteries,proto3" json:"batteries,omitempty"`
 | 
			
		||||
	BaromRelativeIn     *float64               `protobuf:"fixed64,23,opt,name=barom_relative_in,json=baromRelativeIn,proto3,oneof" json:"barom_relative_in,omitempty"`
 | 
			
		||||
	BaromAbsoluteIn     *float64               `protobuf:"fixed64,24,opt,name=barom_absolute_in,json=baromAbsoluteIn,proto3,oneof" json:"barom_absolute_in,omitempty"`
 | 
			
		||||
	DewPointF           *float64               `protobuf:"fixed64,25,opt,name=dew_point_f,json=dewPointF,proto3,oneof" json:"dew_point_f,omitempty"`
 | 
			
		||||
	WindChillF          *float64               `protobuf:"fixed64,26,opt,name=wind_chill_f,json=windChillF,proto3,oneof" json:"wind_chill_f,omitempty"`
 | 
			
		||||
	TempHumiditySensors []*TempHumiditySensor  `protobuf:"bytes,27,rep,name=temp_humidity_sensors,json=tempHumiditySensors,proto3" json:"temp_humidity_sensors,omitempty"`
 | 
			
		||||
	unknownFields       protoimpl.UnknownFields
 | 
			
		||||
	sizeCache           protoimpl.SizeCache
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (x *WeatherUpdate) Reset() {
 | 
			
		||||
	*x = WeatherUpdate{}
 | 
			
		||||
	mi := &file_weather_weather_proto_msgTypes[3]
 | 
			
		||||
	ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 | 
			
		||||
	ms.StoreMessageInfo(mi)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (x *WeatherUpdate) String() string {
 | 
			
		||||
	return protoimpl.X.MessageStringOf(x)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (*WeatherUpdate) ProtoMessage() {}
 | 
			
		||||
 | 
			
		||||
func (x *WeatherUpdate) ProtoReflect() protoreflect.Message {
 | 
			
		||||
	mi := &file_weather_weather_proto_msgTypes[3]
 | 
			
		||||
	if x != nil {
 | 
			
		||||
		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 | 
			
		||||
		if ms.LoadMessageInfo() == nil {
 | 
			
		||||
			ms.StoreMessageInfo(mi)
 | 
			
		||||
		}
 | 
			
		||||
		return ms
 | 
			
		||||
	}
 | 
			
		||||
	return mi.MessageOf(x)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Deprecated: Use WeatherUpdate.ProtoReflect.Descriptor instead.
 | 
			
		||||
func (*WeatherUpdate) Descriptor() ([]byte, []int) {
 | 
			
		||||
	return file_weather_weather_proto_rawDescGZIP(), []int{3}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (x *WeatherUpdate) GetStationName() string {
 | 
			
		||||
	if x != nil {
 | 
			
		||||
		return x.StationName
 | 
			
		||||
	}
 | 
			
		||||
	return ""
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (x *WeatherUpdate) GetStationType() string {
 | 
			
		||||
	if x != nil {
 | 
			
		||||
		return x.StationType
 | 
			
		||||
	}
 | 
			
		||||
	return ""
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (x *WeatherUpdate) GetStationId() string {
 | 
			
		||||
	if x != nil {
 | 
			
		||||
		return x.StationId
 | 
			
		||||
	}
 | 
			
		||||
	return ""
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (x *WeatherUpdate) GetTempOutdoorF() float64 {
 | 
			
		||||
	if x != nil && x.TempOutdoorF != nil {
 | 
			
		||||
		return *x.TempOutdoorF
 | 
			
		||||
	}
 | 
			
		||||
	return 0
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (x *WeatherUpdate) GetTempIndoorF() float64 {
 | 
			
		||||
	if x != nil && x.TempIndoorF != nil {
 | 
			
		||||
		return *x.TempIndoorF
 | 
			
		||||
	}
 | 
			
		||||
	return 0
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (x *WeatherUpdate) GetHumidityOutdoor() int32 {
 | 
			
		||||
	if x != nil && x.HumidityOutdoor != nil {
 | 
			
		||||
		return *x.HumidityOutdoor
 | 
			
		||||
	}
 | 
			
		||||
	return 0
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (x *WeatherUpdate) GetHumidityIndoor() int32 {
 | 
			
		||||
	if x != nil && x.HumidityIndoor != nil {
 | 
			
		||||
		return *x.HumidityIndoor
 | 
			
		||||
	}
 | 
			
		||||
	return 0
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (x *WeatherUpdate) GetWindSpeedMph() float64 {
 | 
			
		||||
	if x != nil && x.WindSpeedMph != nil {
 | 
			
		||||
		return *x.WindSpeedMph
 | 
			
		||||
	}
 | 
			
		||||
	return 0
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (x *WeatherUpdate) GetWindGustMph() float64 {
 | 
			
		||||
	if x != nil && x.WindGustMph != nil {
 | 
			
		||||
		return *x.WindGustMph
 | 
			
		||||
	}
 | 
			
		||||
	return 0
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (x *WeatherUpdate) GetMaxDailyGust() float64 {
 | 
			
		||||
	if x != nil && x.MaxDailyGust != nil {
 | 
			
		||||
		return *x.MaxDailyGust
 | 
			
		||||
	}
 | 
			
		||||
	return 0
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (x *WeatherUpdate) GetWindDir() int32 {
 | 
			
		||||
	if x != nil && x.WindDir != nil {
 | 
			
		||||
		return *x.WindDir
 | 
			
		||||
	}
 | 
			
		||||
	return 0
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (x *WeatherUpdate) GetWindDirAvg_10M() int32 {
 | 
			
		||||
	if x != nil && x.WindDirAvg_10M != nil {
 | 
			
		||||
		return *x.WindDirAvg_10M
 | 
			
		||||
	}
 | 
			
		||||
	return 0
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (x *WeatherUpdate) GetUv() int32 {
 | 
			
		||||
	if x != nil && x.Uv != nil {
 | 
			
		||||
		return *x.Uv
 | 
			
		||||
	}
 | 
			
		||||
	return 0
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (x *WeatherUpdate) GetSolarRadiation() float64 {
 | 
			
		||||
	if x != nil && x.SolarRadiation != nil {
 | 
			
		||||
		return *x.SolarRadiation
 | 
			
		||||
	}
 | 
			
		||||
	return 0
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (x *WeatherUpdate) GetHourlyRainIn() float64 {
 | 
			
		||||
	if x != nil && x.HourlyRainIn != nil {
 | 
			
		||||
		return *x.HourlyRainIn
 | 
			
		||||
	}
 | 
			
		||||
	return 0
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (x *WeatherUpdate) GetEventRainIn() float64 {
 | 
			
		||||
	if x != nil && x.EventRainIn != nil {
 | 
			
		||||
		return *x.EventRainIn
 | 
			
		||||
	}
 | 
			
		||||
	return 0
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (x *WeatherUpdate) GetDailyRainIn() float64 {
 | 
			
		||||
	if x != nil && x.DailyRainIn != nil {
 | 
			
		||||
		return *x.DailyRainIn
 | 
			
		||||
	}
 | 
			
		||||
	return 0
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (x *WeatherUpdate) GetWeeklyRainIn() float64 {
 | 
			
		||||
	if x != nil && x.WeeklyRainIn != nil {
 | 
			
		||||
		return *x.WeeklyRainIn
 | 
			
		||||
	}
 | 
			
		||||
	return 0
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (x *WeatherUpdate) GetMonthlyRainIn() float64 {
 | 
			
		||||
	if x != nil && x.MonthlyRainIn != nil {
 | 
			
		||||
		return *x.MonthlyRainIn
 | 
			
		||||
	}
 | 
			
		||||
	return 0
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (x *WeatherUpdate) GetYearlyRainIn() float64 {
 | 
			
		||||
	if x != nil && x.YearlyRainIn != nil {
 | 
			
		||||
		return *x.YearlyRainIn
 | 
			
		||||
	}
 | 
			
		||||
	return 0
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (x *WeatherUpdate) GetTotalRainIn() float64 {
 | 
			
		||||
	if x != nil && x.TotalRainIn != nil {
 | 
			
		||||
		return *x.TotalRainIn
 | 
			
		||||
	}
 | 
			
		||||
	return 0
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (x *WeatherUpdate) GetBatteries() []*BatteryStatus {
 | 
			
		||||
	if x != nil {
 | 
			
		||||
		return x.Batteries
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (x *WeatherUpdate) GetBaromRelativeIn() float64 {
 | 
			
		||||
	if x != nil && x.BaromRelativeIn != nil {
 | 
			
		||||
		return *x.BaromRelativeIn
 | 
			
		||||
	}
 | 
			
		||||
	return 0
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (x *WeatherUpdate) GetBaromAbsoluteIn() float64 {
 | 
			
		||||
	if x != nil && x.BaromAbsoluteIn != nil {
 | 
			
		||||
		return *x.BaromAbsoluteIn
 | 
			
		||||
	}
 | 
			
		||||
	return 0
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (x *WeatherUpdate) GetDewPointF() float64 {
 | 
			
		||||
	if x != nil && x.DewPointF != nil {
 | 
			
		||||
		return *x.DewPointF
 | 
			
		||||
	}
 | 
			
		||||
	return 0
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (x *WeatherUpdate) GetWindChillF() float64 {
 | 
			
		||||
	if x != nil && x.WindChillF != nil {
 | 
			
		||||
		return *x.WindChillF
 | 
			
		||||
	}
 | 
			
		||||
	return 0
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (x *WeatherUpdate) GetTempHumiditySensors() []*TempHumiditySensor {
 | 
			
		||||
	if x != nil {
 | 
			
		||||
		return x.TempHumiditySensors
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Represents a temperature and humidity sensor
 | 
			
		||||
type TempHumiditySensor struct {
 | 
			
		||||
	state         protoimpl.MessageState `protogen:"open.v1"`
 | 
			
		||||
	Name          string                 `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
 | 
			
		||||
	TempF         *float64               `protobuf:"fixed64,2,opt,name=temp_f,json=tempF,proto3,oneof" json:"temp_f,omitempty"`
 | 
			
		||||
	Humidity      *int32                 `protobuf:"varint,3,opt,name=humidity,proto3,oneof" json:"humidity,omitempty"`
 | 
			
		||||
	unknownFields protoimpl.UnknownFields
 | 
			
		||||
	sizeCache     protoimpl.SizeCache
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (x *TempHumiditySensor) Reset() {
 | 
			
		||||
	*x = TempHumiditySensor{}
 | 
			
		||||
	mi := &file_weather_weather_proto_msgTypes[4]
 | 
			
		||||
	ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 | 
			
		||||
	ms.StoreMessageInfo(mi)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (x *TempHumiditySensor) String() string {
 | 
			
		||||
	return protoimpl.X.MessageStringOf(x)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (*TempHumiditySensor) ProtoMessage() {}
 | 
			
		||||
 | 
			
		||||
func (x *TempHumiditySensor) ProtoReflect() protoreflect.Message {
 | 
			
		||||
	mi := &file_weather_weather_proto_msgTypes[4]
 | 
			
		||||
	if x != nil {
 | 
			
		||||
		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 | 
			
		||||
		if ms.LoadMessageInfo() == nil {
 | 
			
		||||
			ms.StoreMessageInfo(mi)
 | 
			
		||||
		}
 | 
			
		||||
		return ms
 | 
			
		||||
	}
 | 
			
		||||
	return mi.MessageOf(x)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Deprecated: Use TempHumiditySensor.ProtoReflect.Descriptor instead.
 | 
			
		||||
func (*TempHumiditySensor) Descriptor() ([]byte, []int) {
 | 
			
		||||
	return file_weather_weather_proto_rawDescGZIP(), []int{4}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (x *TempHumiditySensor) GetName() string {
 | 
			
		||||
	if x != nil {
 | 
			
		||||
		return x.Name
 | 
			
		||||
	}
 | 
			
		||||
	return ""
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (x *TempHumiditySensor) GetTempF() float64 {
 | 
			
		||||
	if x != nil && x.TempF != nil {
 | 
			
		||||
		return *x.TempF
 | 
			
		||||
	}
 | 
			
		||||
	return 0
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (x *TempHumiditySensor) GetHumidity() int32 {
 | 
			
		||||
	if x != nil && x.Humidity != nil {
 | 
			
		||||
		return *x.Humidity
 | 
			
		||||
	}
 | 
			
		||||
	return 0
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Represents battery status for different components
 | 
			
		||||
type BatteryStatus struct {
 | 
			
		||||
	state         protoimpl.MessageState `protogen:"open.v1"`
 | 
			
		||||
	Component     string                 `protobuf:"bytes,1,opt,name=component,proto3" json:"component,omitempty"`
 | 
			
		||||
	Status        *int32                 `protobuf:"varint,2,opt,name=status,proto3,oneof" json:"status,omitempty"`
 | 
			
		||||
	unknownFields protoimpl.UnknownFields
 | 
			
		||||
	sizeCache     protoimpl.SizeCache
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (x *BatteryStatus) Reset() {
 | 
			
		||||
	*x = BatteryStatus{}
 | 
			
		||||
	mi := &file_weather_weather_proto_msgTypes[5]
 | 
			
		||||
	ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 | 
			
		||||
	ms.StoreMessageInfo(mi)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (x *BatteryStatus) String() string {
 | 
			
		||||
	return protoimpl.X.MessageStringOf(x)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (*BatteryStatus) ProtoMessage() {}
 | 
			
		||||
 | 
			
		||||
func (x *BatteryStatus) ProtoReflect() protoreflect.Message {
 | 
			
		||||
	mi := &file_weather_weather_proto_msgTypes[5]
 | 
			
		||||
	if x != nil {
 | 
			
		||||
		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 | 
			
		||||
		if ms.LoadMessageInfo() == nil {
 | 
			
		||||
			ms.StoreMessageInfo(mi)
 | 
			
		||||
		}
 | 
			
		||||
		return ms
 | 
			
		||||
	}
 | 
			
		||||
	return mi.MessageOf(x)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Deprecated: Use BatteryStatus.ProtoReflect.Descriptor instead.
 | 
			
		||||
func (*BatteryStatus) Descriptor() ([]byte, []int) {
 | 
			
		||||
	return file_weather_weather_proto_rawDescGZIP(), []int{5}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (x *BatteryStatus) GetComponent() string {
 | 
			
		||||
	if x != nil {
 | 
			
		||||
		return x.Component
 | 
			
		||||
	}
 | 
			
		||||
	return ""
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (x *BatteryStatus) GetStatus() int32 {
 | 
			
		||||
	if x != nil && x.Status != nil {
 | 
			
		||||
		return *x.Status
 | 
			
		||||
	}
 | 
			
		||||
	return 0
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var File_weather_weather_proto protoreflect.FileDescriptor
 | 
			
		||||
 | 
			
		||||
var file_weather_weather_proto_rawDesc = string([]byte{
 | 
			
		||||
	0x0a, 0x15, 0x77, 0x65, 0x61, 0x74, 0x68, 0x65, 0x72, 0x2f, 0x77, 0x65, 0x61, 0x74, 0x68, 0x65,
 | 
			
		||||
	0x72, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x0f, 0x61, 0x6d, 0x62, 0x69, 0x65, 0x6e, 0x74,
 | 
			
		||||
	0x2e, 0x77, 0x65, 0x61, 0x74, 0x68, 0x65, 0x72, 0x1a, 0x1f, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65,
 | 
			
		||||
	0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74,
 | 
			
		||||
	0x61, 0x6d, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x48, 0x0a, 0x11, 0x47, 0x65, 0x74,
 | 
			
		||||
	0x57, 0x65, 0x61, 0x74, 0x68, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x33,
 | 
			
		||||
	0x0a, 0x04, 0x6f, 0x70, 0x74, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x61,
 | 
			
		||||
	0x6d, 0x62, 0x69, 0x65, 0x6e, 0x74, 0x2e, 0x77, 0x65, 0x61, 0x74, 0x68, 0x65, 0x72, 0x2e, 0x47,
 | 
			
		||||
	0x65, 0x74, 0x57, 0x65, 0x61, 0x74, 0x68, 0x65, 0x72, 0x4f, 0x70, 0x74, 0x73, 0x52, 0x04, 0x6f,
 | 
			
		||||
	0x70, 0x74, 0x73, 0x22, 0x9c, 0x01, 0x0a, 0x12, 0x47, 0x65, 0x74, 0x57, 0x65, 0x61, 0x74, 0x68,
 | 
			
		||||
	0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3d, 0x0a, 0x0c, 0x6c, 0x61,
 | 
			
		||||
	0x73, 0x74, 0x5f, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b,
 | 
			
		||||
	0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62,
 | 
			
		||||
	0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0b, 0x6c, 0x61,
 | 
			
		||||
	0x73, 0x74, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x12, 0x47, 0x0a, 0x0f, 0x77, 0x65, 0x61,
 | 
			
		||||
	0x74, 0x68, 0x65, 0x72, 0x5f, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x73, 0x18, 0x02, 0x20, 0x03,
 | 
			
		||||
	0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x61, 0x6d, 0x62, 0x69, 0x65, 0x6e, 0x74, 0x2e, 0x77, 0x65, 0x61,
 | 
			
		||||
	0x74, 0x68, 0x65, 0x72, 0x2e, 0x57, 0x65, 0x61, 0x74, 0x68, 0x65, 0x72, 0x55, 0x70, 0x64, 0x61,
 | 
			
		||||
	0x74, 0x65, 0x52, 0x0e, 0x77, 0x65, 0x61, 0x74, 0x68, 0x65, 0x72, 0x55, 0x70, 0x64, 0x61, 0x74,
 | 
			
		||||
	0x65, 0x73, 0x22, 0x82, 0x01, 0x0a, 0x0e, 0x47, 0x65, 0x74, 0x57, 0x65, 0x61, 0x74, 0x68, 0x65,
 | 
			
		||||
	0x72, 0x4f, 0x70, 0x74, 0x73, 0x12, 0x26, 0x0a, 0x0c, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e,
 | 
			
		||||
	0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x0b, 0x73,
 | 
			
		||||
	0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x4e, 0x61, 0x6d, 0x65, 0x88, 0x01, 0x01, 0x12, 0x26, 0x0a,
 | 
			
		||||
	0x0c, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x02, 0x20,
 | 
			
		||||
	0x01, 0x28, 0x09, 0x48, 0x01, 0x52, 0x0b, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x54, 0x79,
 | 
			
		||||
	0x70, 0x65, 0x88, 0x01, 0x01, 0x42, 0x0f, 0x0a, 0x0d, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f,
 | 
			
		||||
	0x6e, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x42, 0x0f, 0x0a, 0x0d, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x69,
 | 
			
		||||
	0x6f, 0x6e, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x22, 0xb7, 0x0c, 0x0a, 0x0d, 0x57, 0x65, 0x61, 0x74,
 | 
			
		||||
	0x68, 0x65, 0x72, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x12, 0x21, 0x0a, 0x0c, 0x73, 0x74, 0x61,
 | 
			
		||||
	0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52,
 | 
			
		||||
	0x0b, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x21, 0x0a, 0x0c,
 | 
			
		||||
	0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x02, 0x20, 0x01,
 | 
			
		||||
	0x28, 0x09, 0x52, 0x0b, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x54, 0x79, 0x70, 0x65, 0x12,
 | 
			
		||||
	0x1d, 0x0a, 0x0a, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x03, 0x20,
 | 
			
		||||
	0x01, 0x28, 0x09, 0x52, 0x09, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x12, 0x29,
 | 
			
		||||
	0x0a, 0x0e, 0x74, 0x65, 0x6d, 0x70, 0x5f, 0x6f, 0x75, 0x74, 0x64, 0x6f, 0x6f, 0x72, 0x5f, 0x66,
 | 
			
		||||
	0x18, 0x04, 0x20, 0x01, 0x28, 0x01, 0x48, 0x00, 0x52, 0x0c, 0x74, 0x65, 0x6d, 0x70, 0x4f, 0x75,
 | 
			
		||||
	0x74, 0x64, 0x6f, 0x6f, 0x72, 0x46, 0x88, 0x01, 0x01, 0x12, 0x27, 0x0a, 0x0d, 0x74, 0x65, 0x6d,
 | 
			
		||||
	0x70, 0x5f, 0x69, 0x6e, 0x64, 0x6f, 0x6f, 0x72, 0x5f, 0x66, 0x18, 0x05, 0x20, 0x01, 0x28, 0x01,
 | 
			
		||||
	0x48, 0x01, 0x52, 0x0b, 0x74, 0x65, 0x6d, 0x70, 0x49, 0x6e, 0x64, 0x6f, 0x6f, 0x72, 0x46, 0x88,
 | 
			
		||||
	0x01, 0x01, 0x12, 0x2e, 0x0a, 0x10, 0x68, 0x75, 0x6d, 0x69, 0x64, 0x69, 0x74, 0x79, 0x5f, 0x6f,
 | 
			
		||||
	0x75, 0x74, 0x64, 0x6f, 0x6f, 0x72, 0x18, 0x06, 0x20, 0x01, 0x28, 0x05, 0x48, 0x02, 0x52, 0x0f,
 | 
			
		||||
	0x68, 0x75, 0x6d, 0x69, 0x64, 0x69, 0x74, 0x79, 0x4f, 0x75, 0x74, 0x64, 0x6f, 0x6f, 0x72, 0x88,
 | 
			
		||||
	0x01, 0x01, 0x12, 0x2c, 0x0a, 0x0f, 0x68, 0x75, 0x6d, 0x69, 0x64, 0x69, 0x74, 0x79, 0x5f, 0x69,
 | 
			
		||||
	0x6e, 0x64, 0x6f, 0x6f, 0x72, 0x18, 0x07, 0x20, 0x01, 0x28, 0x05, 0x48, 0x03, 0x52, 0x0e, 0x68,
 | 
			
		||||
	0x75, 0x6d, 0x69, 0x64, 0x69, 0x74, 0x79, 0x49, 0x6e, 0x64, 0x6f, 0x6f, 0x72, 0x88, 0x01, 0x01,
 | 
			
		||||
	0x12, 0x29, 0x0a, 0x0e, 0x77, 0x69, 0x6e, 0x64, 0x5f, 0x73, 0x70, 0x65, 0x65, 0x64, 0x5f, 0x6d,
 | 
			
		||||
	0x70, 0x68, 0x18, 0x08, 0x20, 0x01, 0x28, 0x01, 0x48, 0x04, 0x52, 0x0c, 0x77, 0x69, 0x6e, 0x64,
 | 
			
		||||
	0x53, 0x70, 0x65, 0x65, 0x64, 0x4d, 0x70, 0x68, 0x88, 0x01, 0x01, 0x12, 0x27, 0x0a, 0x0d, 0x77,
 | 
			
		||||
	0x69, 0x6e, 0x64, 0x5f, 0x67, 0x75, 0x73, 0x74, 0x5f, 0x6d, 0x70, 0x68, 0x18, 0x09, 0x20, 0x01,
 | 
			
		||||
	0x28, 0x01, 0x48, 0x05, 0x52, 0x0b, 0x77, 0x69, 0x6e, 0x64, 0x47, 0x75, 0x73, 0x74, 0x4d, 0x70,
 | 
			
		||||
	0x68, 0x88, 0x01, 0x01, 0x12, 0x29, 0x0a, 0x0e, 0x6d, 0x61, 0x78, 0x5f, 0x64, 0x61, 0x69, 0x6c,
 | 
			
		||||
	0x79, 0x5f, 0x67, 0x75, 0x73, 0x74, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x01, 0x48, 0x06, 0x52, 0x0c,
 | 
			
		||||
	0x6d, 0x61, 0x78, 0x44, 0x61, 0x69, 0x6c, 0x79, 0x47, 0x75, 0x73, 0x74, 0x88, 0x01, 0x01, 0x12,
 | 
			
		||||
	0x1e, 0x0a, 0x08, 0x77, 0x69, 0x6e, 0x64, 0x5f, 0x64, 0x69, 0x72, 0x18, 0x0b, 0x20, 0x01, 0x28,
 | 
			
		||||
	0x05, 0x48, 0x07, 0x52, 0x07, 0x77, 0x69, 0x6e, 0x64, 0x44, 0x69, 0x72, 0x88, 0x01, 0x01, 0x12,
 | 
			
		||||
	0x2c, 0x0a, 0x10, 0x77, 0x69, 0x6e, 0x64, 0x5f, 0x64, 0x69, 0x72, 0x5f, 0x61, 0x76, 0x67, 0x5f,
 | 
			
		||||
	0x31, 0x30, 0x6d, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x05, 0x48, 0x08, 0x52, 0x0d, 0x77, 0x69, 0x6e,
 | 
			
		||||
	0x64, 0x44, 0x69, 0x72, 0x41, 0x76, 0x67, 0x31, 0x30, 0x6d, 0x88, 0x01, 0x01, 0x12, 0x13, 0x0a,
 | 
			
		||||
	0x02, 0x75, 0x76, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x05, 0x48, 0x09, 0x52, 0x02, 0x75, 0x76, 0x88,
 | 
			
		||||
	0x01, 0x01, 0x12, 0x2c, 0x0a, 0x0f, 0x73, 0x6f, 0x6c, 0x61, 0x72, 0x5f, 0x72, 0x61, 0x64, 0x69,
 | 
			
		||||
	0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x01, 0x48, 0x0a, 0x52, 0x0e, 0x73,
 | 
			
		||||
	0x6f, 0x6c, 0x61, 0x72, 0x52, 0x61, 0x64, 0x69, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x88, 0x01, 0x01,
 | 
			
		||||
	0x12, 0x29, 0x0a, 0x0e, 0x68, 0x6f, 0x75, 0x72, 0x6c, 0x79, 0x5f, 0x72, 0x61, 0x69, 0x6e, 0x5f,
 | 
			
		||||
	0x69, 0x6e, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x01, 0x48, 0x0b, 0x52, 0x0c, 0x68, 0x6f, 0x75, 0x72,
 | 
			
		||||
	0x6c, 0x79, 0x52, 0x61, 0x69, 0x6e, 0x49, 0x6e, 0x88, 0x01, 0x01, 0x12, 0x27, 0x0a, 0x0d, 0x65,
 | 
			
		||||
	0x76, 0x65, 0x6e, 0x74, 0x5f, 0x72, 0x61, 0x69, 0x6e, 0x5f, 0x69, 0x6e, 0x18, 0x10, 0x20, 0x01,
 | 
			
		||||
	0x28, 0x01, 0x48, 0x0c, 0x52, 0x0b, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x52, 0x61, 0x69, 0x6e, 0x49,
 | 
			
		||||
	0x6e, 0x88, 0x01, 0x01, 0x12, 0x27, 0x0a, 0x0d, 0x64, 0x61, 0x69, 0x6c, 0x79, 0x5f, 0x72, 0x61,
 | 
			
		||||
	0x69, 0x6e, 0x5f, 0x69, 0x6e, 0x18, 0x11, 0x20, 0x01, 0x28, 0x01, 0x48, 0x0d, 0x52, 0x0b, 0x64,
 | 
			
		||||
	0x61, 0x69, 0x6c, 0x79, 0x52, 0x61, 0x69, 0x6e, 0x49, 0x6e, 0x88, 0x01, 0x01, 0x12, 0x29, 0x0a,
 | 
			
		||||
	0x0e, 0x77, 0x65, 0x65, 0x6b, 0x6c, 0x79, 0x5f, 0x72, 0x61, 0x69, 0x6e, 0x5f, 0x69, 0x6e, 0x18,
 | 
			
		||||
	0x12, 0x20, 0x01, 0x28, 0x01, 0x48, 0x0e, 0x52, 0x0c, 0x77, 0x65, 0x65, 0x6b, 0x6c, 0x79, 0x52,
 | 
			
		||||
	0x61, 0x69, 0x6e, 0x49, 0x6e, 0x88, 0x01, 0x01, 0x12, 0x2b, 0x0a, 0x0f, 0x6d, 0x6f, 0x6e, 0x74,
 | 
			
		||||
	0x68, 0x6c, 0x79, 0x5f, 0x72, 0x61, 0x69, 0x6e, 0x5f, 0x69, 0x6e, 0x18, 0x13, 0x20, 0x01, 0x28,
 | 
			
		||||
	0x01, 0x48, 0x0f, 0x52, 0x0d, 0x6d, 0x6f, 0x6e, 0x74, 0x68, 0x6c, 0x79, 0x52, 0x61, 0x69, 0x6e,
 | 
			
		||||
	0x49, 0x6e, 0x88, 0x01, 0x01, 0x12, 0x29, 0x0a, 0x0e, 0x79, 0x65, 0x61, 0x72, 0x6c, 0x79, 0x5f,
 | 
			
		||||
	0x72, 0x61, 0x69, 0x6e, 0x5f, 0x69, 0x6e, 0x18, 0x14, 0x20, 0x01, 0x28, 0x01, 0x48, 0x10, 0x52,
 | 
			
		||||
	0x0c, 0x79, 0x65, 0x61, 0x72, 0x6c, 0x79, 0x52, 0x61, 0x69, 0x6e, 0x49, 0x6e, 0x88, 0x01, 0x01,
 | 
			
		||||
	0x12, 0x27, 0x0a, 0x0d, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x5f, 0x72, 0x61, 0x69, 0x6e, 0x5f, 0x69,
 | 
			
		||||
	0x6e, 0x18, 0x15, 0x20, 0x01, 0x28, 0x01, 0x48, 0x11, 0x52, 0x0b, 0x74, 0x6f, 0x74, 0x61, 0x6c,
 | 
			
		||||
	0x52, 0x61, 0x69, 0x6e, 0x49, 0x6e, 0x88, 0x01, 0x01, 0x12, 0x3c, 0x0a, 0x09, 0x62, 0x61, 0x74,
 | 
			
		||||
	0x74, 0x65, 0x72, 0x69, 0x65, 0x73, 0x18, 0x16, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x61,
 | 
			
		||||
	0x6d, 0x62, 0x69, 0x65, 0x6e, 0x74, 0x2e, 0x77, 0x65, 0x61, 0x74, 0x68, 0x65, 0x72, 0x2e, 0x42,
 | 
			
		||||
	0x61, 0x74, 0x74, 0x65, 0x72, 0x79, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x09, 0x62, 0x61,
 | 
			
		||||
	0x74, 0x74, 0x65, 0x72, 0x69, 0x65, 0x73, 0x12, 0x2f, 0x0a, 0x11, 0x62, 0x61, 0x72, 0x6f, 0x6d,
 | 
			
		||||
	0x5f, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x76, 0x65, 0x5f, 0x69, 0x6e, 0x18, 0x17, 0x20, 0x01,
 | 
			
		||||
	0x28, 0x01, 0x48, 0x12, 0x52, 0x0f, 0x62, 0x61, 0x72, 0x6f, 0x6d, 0x52, 0x65, 0x6c, 0x61, 0x74,
 | 
			
		||||
	0x69, 0x76, 0x65, 0x49, 0x6e, 0x88, 0x01, 0x01, 0x12, 0x2f, 0x0a, 0x11, 0x62, 0x61, 0x72, 0x6f,
 | 
			
		||||
	0x6d, 0x5f, 0x61, 0x62, 0x73, 0x6f, 0x6c, 0x75, 0x74, 0x65, 0x5f, 0x69, 0x6e, 0x18, 0x18, 0x20,
 | 
			
		||||
	0x01, 0x28, 0x01, 0x48, 0x13, 0x52, 0x0f, 0x62, 0x61, 0x72, 0x6f, 0x6d, 0x41, 0x62, 0x73, 0x6f,
 | 
			
		||||
	0x6c, 0x75, 0x74, 0x65, 0x49, 0x6e, 0x88, 0x01, 0x01, 0x12, 0x23, 0x0a, 0x0b, 0x64, 0x65, 0x77,
 | 
			
		||||
	0x5f, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x5f, 0x66, 0x18, 0x19, 0x20, 0x01, 0x28, 0x01, 0x48, 0x14,
 | 
			
		||||
	0x52, 0x09, 0x64, 0x65, 0x77, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x46, 0x88, 0x01, 0x01, 0x12, 0x25,
 | 
			
		||||
	0x0a, 0x0c, 0x77, 0x69, 0x6e, 0x64, 0x5f, 0x63, 0x68, 0x69, 0x6c, 0x6c, 0x5f, 0x66, 0x18, 0x1a,
 | 
			
		||||
	0x20, 0x01, 0x28, 0x01, 0x48, 0x15, 0x52, 0x0a, 0x77, 0x69, 0x6e, 0x64, 0x43, 0x68, 0x69, 0x6c,
 | 
			
		||||
	0x6c, 0x46, 0x88, 0x01, 0x01, 0x12, 0x57, 0x0a, 0x15, 0x74, 0x65, 0x6d, 0x70, 0x5f, 0x68, 0x75,
 | 
			
		||||
	0x6d, 0x69, 0x64, 0x69, 0x74, 0x79, 0x5f, 0x73, 0x65, 0x6e, 0x73, 0x6f, 0x72, 0x73, 0x18, 0x1b,
 | 
			
		||||
	0x20, 0x03, 0x28, 0x0b, 0x32, 0x23, 0x2e, 0x61, 0x6d, 0x62, 0x69, 0x65, 0x6e, 0x74, 0x2e, 0x77,
 | 
			
		||||
	0x65, 0x61, 0x74, 0x68, 0x65, 0x72, 0x2e, 0x54, 0x65, 0x6d, 0x70, 0x48, 0x75, 0x6d, 0x69, 0x64,
 | 
			
		||||
	0x69, 0x74, 0x79, 0x53, 0x65, 0x6e, 0x73, 0x6f, 0x72, 0x52, 0x13, 0x74, 0x65, 0x6d, 0x70, 0x48,
 | 
			
		||||
	0x75, 0x6d, 0x69, 0x64, 0x69, 0x74, 0x79, 0x53, 0x65, 0x6e, 0x73, 0x6f, 0x72, 0x73, 0x42, 0x11,
 | 
			
		||||
	0x0a, 0x0f, 0x5f, 0x74, 0x65, 0x6d, 0x70, 0x5f, 0x6f, 0x75, 0x74, 0x64, 0x6f, 0x6f, 0x72, 0x5f,
 | 
			
		||||
	0x66, 0x42, 0x10, 0x0a, 0x0e, 0x5f, 0x74, 0x65, 0x6d, 0x70, 0x5f, 0x69, 0x6e, 0x64, 0x6f, 0x6f,
 | 
			
		||||
	0x72, 0x5f, 0x66, 0x42, 0x13, 0x0a, 0x11, 0x5f, 0x68, 0x75, 0x6d, 0x69, 0x64, 0x69, 0x74, 0x79,
 | 
			
		||||
	0x5f, 0x6f, 0x75, 0x74, 0x64, 0x6f, 0x6f, 0x72, 0x42, 0x12, 0x0a, 0x10, 0x5f, 0x68, 0x75, 0x6d,
 | 
			
		||||
	0x69, 0x64, 0x69, 0x74, 0x79, 0x5f, 0x69, 0x6e, 0x64, 0x6f, 0x6f, 0x72, 0x42, 0x11, 0x0a, 0x0f,
 | 
			
		||||
	0x5f, 0x77, 0x69, 0x6e, 0x64, 0x5f, 0x73, 0x70, 0x65, 0x65, 0x64, 0x5f, 0x6d, 0x70, 0x68, 0x42,
 | 
			
		||||
	0x10, 0x0a, 0x0e, 0x5f, 0x77, 0x69, 0x6e, 0x64, 0x5f, 0x67, 0x75, 0x73, 0x74, 0x5f, 0x6d, 0x70,
 | 
			
		||||
	0x68, 0x42, 0x11, 0x0a, 0x0f, 0x5f, 0x6d, 0x61, 0x78, 0x5f, 0x64, 0x61, 0x69, 0x6c, 0x79, 0x5f,
 | 
			
		||||
	0x67, 0x75, 0x73, 0x74, 0x42, 0x0b, 0x0a, 0x09, 0x5f, 0x77, 0x69, 0x6e, 0x64, 0x5f, 0x64, 0x69,
 | 
			
		||||
	0x72, 0x42, 0x13, 0x0a, 0x11, 0x5f, 0x77, 0x69, 0x6e, 0x64, 0x5f, 0x64, 0x69, 0x72, 0x5f, 0x61,
 | 
			
		||||
	0x76, 0x67, 0x5f, 0x31, 0x30, 0x6d, 0x42, 0x05, 0x0a, 0x03, 0x5f, 0x75, 0x76, 0x42, 0x12, 0x0a,
 | 
			
		||||
	0x10, 0x5f, 0x73, 0x6f, 0x6c, 0x61, 0x72, 0x5f, 0x72, 0x61, 0x64, 0x69, 0x61, 0x74, 0x69, 0x6f,
 | 
			
		||||
	0x6e, 0x42, 0x11, 0x0a, 0x0f, 0x5f, 0x68, 0x6f, 0x75, 0x72, 0x6c, 0x79, 0x5f, 0x72, 0x61, 0x69,
 | 
			
		||||
	0x6e, 0x5f, 0x69, 0x6e, 0x42, 0x10, 0x0a, 0x0e, 0x5f, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x5f, 0x72,
 | 
			
		||||
	0x61, 0x69, 0x6e, 0x5f, 0x69, 0x6e, 0x42, 0x10, 0x0a, 0x0e, 0x5f, 0x64, 0x61, 0x69, 0x6c, 0x79,
 | 
			
		||||
	0x5f, 0x72, 0x61, 0x69, 0x6e, 0x5f, 0x69, 0x6e, 0x42, 0x11, 0x0a, 0x0f, 0x5f, 0x77, 0x65, 0x65,
 | 
			
		||||
	0x6b, 0x6c, 0x79, 0x5f, 0x72, 0x61, 0x69, 0x6e, 0x5f, 0x69, 0x6e, 0x42, 0x12, 0x0a, 0x10, 0x5f,
 | 
			
		||||
	0x6d, 0x6f, 0x6e, 0x74, 0x68, 0x6c, 0x79, 0x5f, 0x72, 0x61, 0x69, 0x6e, 0x5f, 0x69, 0x6e, 0x42,
 | 
			
		||||
	0x11, 0x0a, 0x0f, 0x5f, 0x79, 0x65, 0x61, 0x72, 0x6c, 0x79, 0x5f, 0x72, 0x61, 0x69, 0x6e, 0x5f,
 | 
			
		||||
	0x69, 0x6e, 0x42, 0x10, 0x0a, 0x0e, 0x5f, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x5f, 0x72, 0x61, 0x69,
 | 
			
		||||
	0x6e, 0x5f, 0x69, 0x6e, 0x42, 0x14, 0x0a, 0x12, 0x5f, 0x62, 0x61, 0x72, 0x6f, 0x6d, 0x5f, 0x72,
 | 
			
		||||
	0x65, 0x6c, 0x61, 0x74, 0x69, 0x76, 0x65, 0x5f, 0x69, 0x6e, 0x42, 0x14, 0x0a, 0x12, 0x5f, 0x62,
 | 
			
		||||
	0x61, 0x72, 0x6f, 0x6d, 0x5f, 0x61, 0x62, 0x73, 0x6f, 0x6c, 0x75, 0x74, 0x65, 0x5f, 0x69, 0x6e,
 | 
			
		||||
	0x42, 0x0e, 0x0a, 0x0c, 0x5f, 0x64, 0x65, 0x77, 0x5f, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x5f, 0x66,
 | 
			
		||||
	0x42, 0x0f, 0x0a, 0x0d, 0x5f, 0x77, 0x69, 0x6e, 0x64, 0x5f, 0x63, 0x68, 0x69, 0x6c, 0x6c, 0x5f,
 | 
			
		||||
	0x66, 0x22, 0x7d, 0x0a, 0x12, 0x54, 0x65, 0x6d, 0x70, 0x48, 0x75, 0x6d, 0x69, 0x64, 0x69, 0x74,
 | 
			
		||||
	0x79, 0x53, 0x65, 0x6e, 0x73, 0x6f, 0x72, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18,
 | 
			
		||||
	0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1a, 0x0a, 0x06, 0x74,
 | 
			
		||||
	0x65, 0x6d, 0x70, 0x5f, 0x66, 0x18, 0x02, 0x20, 0x01, 0x28, 0x01, 0x48, 0x00, 0x52, 0x05, 0x74,
 | 
			
		||||
	0x65, 0x6d, 0x70, 0x46, 0x88, 0x01, 0x01, 0x12, 0x1f, 0x0a, 0x08, 0x68, 0x75, 0x6d, 0x69, 0x64,
 | 
			
		||||
	0x69, 0x74, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, 0x48, 0x01, 0x52, 0x08, 0x68, 0x75, 0x6d,
 | 
			
		||||
	0x69, 0x64, 0x69, 0x74, 0x79, 0x88, 0x01, 0x01, 0x42, 0x09, 0x0a, 0x07, 0x5f, 0x74, 0x65, 0x6d,
 | 
			
		||||
	0x70, 0x5f, 0x66, 0x42, 0x0b, 0x0a, 0x09, 0x5f, 0x68, 0x75, 0x6d, 0x69, 0x64, 0x69, 0x74, 0x79,
 | 
			
		||||
	0x22, 0x55, 0x0a, 0x0d, 0x42, 0x61, 0x74, 0x74, 0x65, 0x72, 0x79, 0x53, 0x74, 0x61, 0x74, 0x75,
 | 
			
		||||
	0x73, 0x12, 0x1c, 0x0a, 0x09, 0x63, 0x6f, 0x6d, 0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x18, 0x01,
 | 
			
		||||
	0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x63, 0x6f, 0x6d, 0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x12,
 | 
			
		||||
	0x1b, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x48,
 | 
			
		||||
	0x00, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x88, 0x01, 0x01, 0x42, 0x09, 0x0a, 0x07,
 | 
			
		||||
	0x5f, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x42, 0x54, 0x5a, 0x52, 0x67, 0x69, 0x74, 0x65, 0x61,
 | 
			
		||||
	0x2e, 0x6c, 0x69, 0x62, 0x72, 0x65, 0x74, 0x65, 0x63, 0x68, 0x63, 0x6f, 0x6e, 0x73, 0x75, 0x6c,
 | 
			
		||||
	0x74, 0x69, 0x6e, 0x67, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x72, 0x6d, 0x63, 0x67, 0x75, 0x69, 0x72,
 | 
			
		||||
	0x65, 0x2f, 0x61, 0x6d, 0x62, 0x69, 0x65, 0x6e, 0x74, 0x2d, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x2d,
 | 
			
		||||
	0x65, 0x78, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x72, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, 0x61,
 | 
			
		||||
	0x6c, 0x70, 0x68, 0x61, 0x31, 0x2f, 0x77, 0x65, 0x61, 0x74, 0x68, 0x65, 0x72, 0x62, 0x06, 0x70,
 | 
			
		||||
	0x72, 0x6f, 0x74, 0x6f, 0x33,
 | 
			
		||||
})
 | 
			
		||||
 | 
			
		||||
var (
 | 
			
		||||
	file_weather_weather_proto_rawDescOnce sync.Once
 | 
			
		||||
	file_weather_weather_proto_rawDescData []byte
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func file_weather_weather_proto_rawDescGZIP() []byte {
 | 
			
		||||
	file_weather_weather_proto_rawDescOnce.Do(func() {
 | 
			
		||||
		file_weather_weather_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_weather_weather_proto_rawDesc), len(file_weather_weather_proto_rawDesc)))
 | 
			
		||||
	})
 | 
			
		||||
	return file_weather_weather_proto_rawDescData
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var file_weather_weather_proto_msgTypes = make([]protoimpl.MessageInfo, 6)
 | 
			
		||||
var file_weather_weather_proto_goTypes = []any{
 | 
			
		||||
	(*GetWeatherRequest)(nil),     // 0: ambient.weather.GetWeatherRequest
 | 
			
		||||
	(*GetWeatherResponse)(nil),    // 1: ambient.weather.GetWeatherResponse
 | 
			
		||||
	(*GetWeatherOpts)(nil),        // 2: ambient.weather.GetWeatherOpts
 | 
			
		||||
	(*WeatherUpdate)(nil),         // 3: ambient.weather.WeatherUpdate
 | 
			
		||||
	(*TempHumiditySensor)(nil),    // 4: ambient.weather.TempHumiditySensor
 | 
			
		||||
	(*BatteryStatus)(nil),         // 5: ambient.weather.BatteryStatus
 | 
			
		||||
	(*timestamppb.Timestamp)(nil), // 6: google.protobuf.Timestamp
 | 
			
		||||
}
 | 
			
		||||
var file_weather_weather_proto_depIdxs = []int32{
 | 
			
		||||
	2, // 0: ambient.weather.GetWeatherRequest.opts:type_name -> ambient.weather.GetWeatherOpts
 | 
			
		||||
	6, // 1: ambient.weather.GetWeatherResponse.last_updated:type_name -> google.protobuf.Timestamp
 | 
			
		||||
	3, // 2: ambient.weather.GetWeatherResponse.weather_updates:type_name -> ambient.weather.WeatherUpdate
 | 
			
		||||
	5, // 3: ambient.weather.WeatherUpdate.batteries:type_name -> ambient.weather.BatteryStatus
 | 
			
		||||
	4, // 4: ambient.weather.WeatherUpdate.temp_humidity_sensors:type_name -> ambient.weather.TempHumiditySensor
 | 
			
		||||
	5, // [5:5] is the sub-list for method output_type
 | 
			
		||||
	5, // [5:5] is the sub-list for method input_type
 | 
			
		||||
	5, // [5:5] is the sub-list for extension type_name
 | 
			
		||||
	5, // [5:5] is the sub-list for extension extendee
 | 
			
		||||
	0, // [0:5] is the sub-list for field type_name
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func init() { file_weather_weather_proto_init() }
 | 
			
		||||
func file_weather_weather_proto_init() {
 | 
			
		||||
	if File_weather_weather_proto != nil {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	file_weather_weather_proto_msgTypes[2].OneofWrappers = []any{}
 | 
			
		||||
	file_weather_weather_proto_msgTypes[3].OneofWrappers = []any{}
 | 
			
		||||
	file_weather_weather_proto_msgTypes[4].OneofWrappers = []any{}
 | 
			
		||||
	file_weather_weather_proto_msgTypes[5].OneofWrappers = []any{}
 | 
			
		||||
	type x struct{}
 | 
			
		||||
	out := protoimpl.TypeBuilder{
 | 
			
		||||
		File: protoimpl.DescBuilder{
 | 
			
		||||
			GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
 | 
			
		||||
			RawDescriptor: unsafe.Slice(unsafe.StringData(file_weather_weather_proto_rawDesc), len(file_weather_weather_proto_rawDesc)),
 | 
			
		||||
			NumEnums:      0,
 | 
			
		||||
			NumMessages:   6,
 | 
			
		||||
			NumExtensions: 0,
 | 
			
		||||
			NumServices:   0,
 | 
			
		||||
		},
 | 
			
		||||
		GoTypes:           file_weather_weather_proto_goTypes,
 | 
			
		||||
		DependencyIndexes: file_weather_weather_proto_depIdxs,
 | 
			
		||||
		MessageInfos:      file_weather_weather_proto_msgTypes,
 | 
			
		||||
	}.Build()
 | 
			
		||||
	File_weather_weather_proto = out.File
 | 
			
		||||
	file_weather_weather_proto_goTypes = nil
 | 
			
		||||
	file_weather_weather_proto_depIdxs = nil
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										82
									
								
								api/v1alpha1/weather/weather_service.pb.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										82
									
								
								api/v1alpha1/weather/weather_service.pb.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,82 @@
 | 
			
		||||
// Code generated by protoc-gen-go. DO NOT EDIT.
 | 
			
		||||
// versions:
 | 
			
		||||
// 	protoc-gen-go v1.36.5
 | 
			
		||||
// 	protoc        v5.29.3
 | 
			
		||||
// source: weather/weather_service.proto
 | 
			
		||||
 | 
			
		||||
package weather
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	protoreflect "google.golang.org/protobuf/reflect/protoreflect"
 | 
			
		||||
	protoimpl "google.golang.org/protobuf/runtime/protoimpl"
 | 
			
		||||
	reflect "reflect"
 | 
			
		||||
	unsafe "unsafe"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	// Verify that this generated code is sufficiently up-to-date.
 | 
			
		||||
	_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
 | 
			
		||||
	// Verify that runtime/protoimpl is sufficiently up-to-date.
 | 
			
		||||
	_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var File_weather_weather_service_proto protoreflect.FileDescriptor
 | 
			
		||||
 | 
			
		||||
var file_weather_weather_service_proto_rawDesc = string([]byte{
 | 
			
		||||
	0x0a, 0x1d, 0x77, 0x65, 0x61, 0x74, 0x68, 0x65, 0x72, 0x2f, 0x77, 0x65, 0x61, 0x74, 0x68, 0x65,
 | 
			
		||||
	0x72, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12,
 | 
			
		||||
	0x0f, 0x61, 0x6d, 0x62, 0x69, 0x65, 0x6e, 0x74, 0x2e, 0x77, 0x65, 0x61, 0x74, 0x68, 0x65, 0x72,
 | 
			
		||||
	0x1a, 0x15, 0x77, 0x65, 0x61, 0x74, 0x68, 0x65, 0x72, 0x2f, 0x77, 0x65, 0x61, 0x74, 0x68, 0x65,
 | 
			
		||||
	0x72, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x32, 0x73, 0x0a, 0x1a, 0x41, 0x6d, 0x62, 0x69, 0x65,
 | 
			
		||||
	0x6e, 0x74, 0x4c, 0x6f, 0x63, 0x61, 0x6c, 0x57, 0x65, 0x61, 0x74, 0x68, 0x65, 0x72, 0x53, 0x65,
 | 
			
		||||
	0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x55, 0x0a, 0x0a, 0x47, 0x65, 0x74, 0x57, 0x65, 0x61, 0x74,
 | 
			
		||||
	0x68, 0x65, 0x72, 0x12, 0x22, 0x2e, 0x61, 0x6d, 0x62, 0x69, 0x65, 0x6e, 0x74, 0x2e, 0x77, 0x65,
 | 
			
		||||
	0x61, 0x74, 0x68, 0x65, 0x72, 0x2e, 0x47, 0x65, 0x74, 0x57, 0x65, 0x61, 0x74, 0x68, 0x65, 0x72,
 | 
			
		||||
	0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x23, 0x2e, 0x61, 0x6d, 0x62, 0x69, 0x65, 0x6e,
 | 
			
		||||
	0x74, 0x2e, 0x77, 0x65, 0x61, 0x74, 0x68, 0x65, 0x72, 0x2e, 0x47, 0x65, 0x74, 0x57, 0x65, 0x61,
 | 
			
		||||
	0x74, 0x68, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x54, 0x5a, 0x52,
 | 
			
		||||
	0x67, 0x69, 0x74, 0x65, 0x61, 0x2e, 0x6c, 0x69, 0x62, 0x72, 0x65, 0x74, 0x65, 0x63, 0x68, 0x63,
 | 
			
		||||
	0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x74, 0x69, 0x6e, 0x67, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x72, 0x6d,
 | 
			
		||||
	0x63, 0x67, 0x75, 0x69, 0x72, 0x65, 0x2f, 0x61, 0x6d, 0x62, 0x69, 0x65, 0x6e, 0x74, 0x2d, 0x6c,
 | 
			
		||||
	0x6f, 0x63, 0x61, 0x6c, 0x2d, 0x65, 0x78, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x72, 0x2f, 0x61, 0x70,
 | 
			
		||||
	0x69, 0x2f, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2f, 0x77, 0x65, 0x61, 0x74, 0x68,
 | 
			
		||||
	0x65, 0x72, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
 | 
			
		||||
})
 | 
			
		||||
 | 
			
		||||
var file_weather_weather_service_proto_goTypes = []any{
 | 
			
		||||
	(*GetWeatherRequest)(nil),  // 0: ambient.weather.GetWeatherRequest
 | 
			
		||||
	(*GetWeatherResponse)(nil), // 1: ambient.weather.GetWeatherResponse
 | 
			
		||||
}
 | 
			
		||||
var file_weather_weather_service_proto_depIdxs = []int32{
 | 
			
		||||
	0, // 0: ambient.weather.AmbientLocalWeatherService.GetWeather:input_type -> ambient.weather.GetWeatherRequest
 | 
			
		||||
	1, // 1: ambient.weather.AmbientLocalWeatherService.GetWeather:output_type -> ambient.weather.GetWeatherResponse
 | 
			
		||||
	1, // [1:2] is the sub-list for method output_type
 | 
			
		||||
	0, // [0:1] is the sub-list for method input_type
 | 
			
		||||
	0, // [0:0] is the sub-list for extension type_name
 | 
			
		||||
	0, // [0:0] is the sub-list for extension extendee
 | 
			
		||||
	0, // [0:0] is the sub-list for field type_name
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func init() { file_weather_weather_service_proto_init() }
 | 
			
		||||
func file_weather_weather_service_proto_init() {
 | 
			
		||||
	if File_weather_weather_service_proto != nil {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	file_weather_weather_proto_init()
 | 
			
		||||
	type x struct{}
 | 
			
		||||
	out := protoimpl.TypeBuilder{
 | 
			
		||||
		File: protoimpl.DescBuilder{
 | 
			
		||||
			GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
 | 
			
		||||
			RawDescriptor: unsafe.Slice(unsafe.StringData(file_weather_weather_service_proto_rawDesc), len(file_weather_weather_service_proto_rawDesc)),
 | 
			
		||||
			NumEnums:      0,
 | 
			
		||||
			NumMessages:   0,
 | 
			
		||||
			NumExtensions: 0,
 | 
			
		||||
			NumServices:   1,
 | 
			
		||||
		},
 | 
			
		||||
		GoTypes:           file_weather_weather_service_proto_goTypes,
 | 
			
		||||
		DependencyIndexes: file_weather_weather_service_proto_depIdxs,
 | 
			
		||||
	}.Build()
 | 
			
		||||
	File_weather_weather_service_proto = out.File
 | 
			
		||||
	file_weather_weather_service_proto_goTypes = nil
 | 
			
		||||
	file_weather_weather_service_proto_depIdxs = nil
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										122
									
								
								api/v1alpha1/weather/weather_service_grpc.pb.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										122
									
								
								api/v1alpha1/weather/weather_service_grpc.pb.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,122 @@
 | 
			
		||||
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
 | 
			
		||||
// versions:
 | 
			
		||||
// - protoc-gen-go-grpc v1.5.1
 | 
			
		||||
// - protoc             v5.29.3
 | 
			
		||||
// source: weather/weather_service.proto
 | 
			
		||||
 | 
			
		||||
package weather
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	context "context"
 | 
			
		||||
	grpc "google.golang.org/grpc"
 | 
			
		||||
	codes "google.golang.org/grpc/codes"
 | 
			
		||||
	status "google.golang.org/grpc/status"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// This is a compile-time assertion to ensure that this generated file
 | 
			
		||||
// is compatible with the grpc package it is being compiled against.
 | 
			
		||||
// Requires gRPC-Go v1.64.0 or later.
 | 
			
		||||
const _ = grpc.SupportPackageIsVersion9
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	AmbientLocalWeatherService_GetWeather_FullMethodName = "/ambient.weather.AmbientLocalWeatherService/GetWeather"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// AmbientLocalWeatherServiceClient is the client API for AmbientLocalWeatherService service.
 | 
			
		||||
//
 | 
			
		||||
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.
 | 
			
		||||
type AmbientLocalWeatherServiceClient interface {
 | 
			
		||||
	GetWeather(ctx context.Context, in *GetWeatherRequest, opts ...grpc.CallOption) (*GetWeatherResponse, error)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type ambientLocalWeatherServiceClient struct {
 | 
			
		||||
	cc grpc.ClientConnInterface
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func NewAmbientLocalWeatherServiceClient(cc grpc.ClientConnInterface) AmbientLocalWeatherServiceClient {
 | 
			
		||||
	return &ambientLocalWeatherServiceClient{cc}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (c *ambientLocalWeatherServiceClient) GetWeather(ctx context.Context, in *GetWeatherRequest, opts ...grpc.CallOption) (*GetWeatherResponse, error) {
 | 
			
		||||
	cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
 | 
			
		||||
	out := new(GetWeatherResponse)
 | 
			
		||||
	err := c.cc.Invoke(ctx, AmbientLocalWeatherService_GetWeather_FullMethodName, in, out, cOpts...)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	return out, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// AmbientLocalWeatherServiceServer is the server API for AmbientLocalWeatherService service.
 | 
			
		||||
// All implementations must embed UnimplementedAmbientLocalWeatherServiceServer
 | 
			
		||||
// for forward compatibility.
 | 
			
		||||
type AmbientLocalWeatherServiceServer interface {
 | 
			
		||||
	GetWeather(context.Context, *GetWeatherRequest) (*GetWeatherResponse, error)
 | 
			
		||||
	mustEmbedUnimplementedAmbientLocalWeatherServiceServer()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// UnimplementedAmbientLocalWeatherServiceServer must be embedded to have
 | 
			
		||||
// forward compatible implementations.
 | 
			
		||||
//
 | 
			
		||||
// NOTE: this should be embedded by value instead of pointer to avoid a nil
 | 
			
		||||
// pointer dereference when methods are called.
 | 
			
		||||
type UnimplementedAmbientLocalWeatherServiceServer struct{}
 | 
			
		||||
 | 
			
		||||
func (UnimplementedAmbientLocalWeatherServiceServer) GetWeather(context.Context, *GetWeatherRequest) (*GetWeatherResponse, error) {
 | 
			
		||||
	return nil, status.Errorf(codes.Unimplemented, "method GetWeather not implemented")
 | 
			
		||||
}
 | 
			
		||||
func (UnimplementedAmbientLocalWeatherServiceServer) mustEmbedUnimplementedAmbientLocalWeatherServiceServer() {
 | 
			
		||||
}
 | 
			
		||||
func (UnimplementedAmbientLocalWeatherServiceServer) testEmbeddedByValue() {}
 | 
			
		||||
 | 
			
		||||
// UnsafeAmbientLocalWeatherServiceServer may be embedded to opt out of forward compatibility for this service.
 | 
			
		||||
// Use of this interface is not recommended, as added methods to AmbientLocalWeatherServiceServer will
 | 
			
		||||
// result in compilation errors.
 | 
			
		||||
type UnsafeAmbientLocalWeatherServiceServer interface {
 | 
			
		||||
	mustEmbedUnimplementedAmbientLocalWeatherServiceServer()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func RegisterAmbientLocalWeatherServiceServer(s grpc.ServiceRegistrar, srv AmbientLocalWeatherServiceServer) {
 | 
			
		||||
	// If the following call pancis, it indicates UnimplementedAmbientLocalWeatherServiceServer was
 | 
			
		||||
	// embedded by pointer and is nil.  This will cause panics if an
 | 
			
		||||
	// unimplemented method is ever invoked, so we test this at initialization
 | 
			
		||||
	// time to prevent it from happening at runtime later due to I/O.
 | 
			
		||||
	if t, ok := srv.(interface{ testEmbeddedByValue() }); ok {
 | 
			
		||||
		t.testEmbeddedByValue()
 | 
			
		||||
	}
 | 
			
		||||
	s.RegisterService(&AmbientLocalWeatherService_ServiceDesc, srv)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func _AmbientLocalWeatherService_GetWeather_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
 | 
			
		||||
	in := new(GetWeatherRequest)
 | 
			
		||||
	if err := dec(in); err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	if interceptor == nil {
 | 
			
		||||
		return srv.(AmbientLocalWeatherServiceServer).GetWeather(ctx, in)
 | 
			
		||||
	}
 | 
			
		||||
	info := &grpc.UnaryServerInfo{
 | 
			
		||||
		Server:     srv,
 | 
			
		||||
		FullMethod: AmbientLocalWeatherService_GetWeather_FullMethodName,
 | 
			
		||||
	}
 | 
			
		||||
	handler := func(ctx context.Context, req interface{}) (interface{}, error) {
 | 
			
		||||
		return srv.(AmbientLocalWeatherServiceServer).GetWeather(ctx, req.(*GetWeatherRequest))
 | 
			
		||||
	}
 | 
			
		||||
	return interceptor(ctx, in, info, handler)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// AmbientLocalWeatherService_ServiceDesc is the grpc.ServiceDesc for AmbientLocalWeatherService service.
 | 
			
		||||
// It's only intended for direct use with grpc.RegisterService,
 | 
			
		||||
// and not to be introspected or modified (even as a copy)
 | 
			
		||||
var AmbientLocalWeatherService_ServiceDesc = grpc.ServiceDesc{
 | 
			
		||||
	ServiceName: "ambient.weather.AmbientLocalWeatherService",
 | 
			
		||||
	HandlerType: (*AmbientLocalWeatherServiceServer)(nil),
 | 
			
		||||
	Methods: []grpc.MethodDesc{
 | 
			
		||||
		{
 | 
			
		||||
			MethodName: "GetWeather",
 | 
			
		||||
			Handler:    _AmbientLocalWeatherService_GetWeather_Handler,
 | 
			
		||||
		},
 | 
			
		||||
	},
 | 
			
		||||
	Streams:  []grpc.StreamDesc{},
 | 
			
		||||
	Metadata: "weather/weather_service.proto",
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										26
									
								
								docker-compose-sample.yml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								docker-compose-sample.yml
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,26 @@
 | 
			
		||||
name: Ambient Local Exporter
 | 
			
		||||
services:
 | 
			
		||||
  ambient-local-exporter:
 | 
			
		||||
    build: .
 | 
			
		||||
    ports:
 | 
			
		||||
      - 8080:8080
 | 
			
		||||
    volumes:
 | 
			
		||||
      - ./config.yaml:/app/config.yaml
 | 
			
		||||
    command:
 | 
			
		||||
      - -config
 | 
			
		||||
      - /app/config.yaml
 | 
			
		||||
    environment:
 | 
			
		||||
      APP_NAME: ambient-local-exporter
 | 
			
		||||
      APP_LOG_LEVEL: debug ## For testing only
 | 
			
		||||
      APP_LOG_FORMAT: json ## console, json
 | 
			
		||||
      APP_LOG_TIME_FORMAT: rfc3339 ## long, short, unix, rfc3339, off
 | 
			
		||||
      APP_HTTP_LISTEN: 0.0.0.0:8080
 | 
			
		||||
      APP_HTTP_READ_TIMEOUT: 10s
 | 
			
		||||
      APP_HTTP_WRITE_TIMEOUT: 10s
 | 
			
		||||
      APP_HTTP_IDLE_TIMEOUT: 30s
 | 
			
		||||
      APP_HTTP_LOG_REQUESTS: true
 | 
			
		||||
      APP_OTEL_STDOUT_ENABLED: false
 | 
			
		||||
      APP_OTEL_METRIC_INTERVAL_SECS: 30
 | 
			
		||||
      OTEL_EXPORTER_OTLP_ENDPOINT: https://otel.libretechconsulting.com:4317 # Set to your otel collector
 | 
			
		||||
      OTEL_SERVICE_NAME: ambient-local-exporter
 | 
			
		||||
      OTEL_RESOURCE_ATTRIBUTES: "env=development,service.version=(devel)"
 | 
			
		||||
							
								
								
									
										50
									
								
								go.mod
									
									
									
									
									
								
							
							
						
						
									
										50
									
								
								go.mod
									
									
									
									
									
								
							@@ -1,15 +1,18 @@
 | 
			
		||||
module gitea.libretechconsulting.com/rmcguire/ambient-weather-local-exporter
 | 
			
		||||
module gitea.libretechconsulting.com/rmcguire/ambient-local-exporter
 | 
			
		||||
 | 
			
		||||
go 1.23.4
 | 
			
		||||
 | 
			
		||||
require (
 | 
			
		||||
	gitea.libretechconsulting.com/rmcguire/go-app v0.4.1
 | 
			
		||||
	gitea.libretechconsulting.com/rmcguire/go-app v0.6.3
 | 
			
		||||
	github.com/go-resty/resty/v2 v2.16.5
 | 
			
		||||
	github.com/gorilla/schema v1.4.1
 | 
			
		||||
	github.com/rs/zerolog v1.33.0
 | 
			
		||||
	go.opentelemetry.io/otel v1.34.0
 | 
			
		||||
	go.opentelemetry.io/otel/metric v1.34.0
 | 
			
		||||
	golang.org/x/sys v0.29.0
 | 
			
		||||
	go.opentelemetry.io/otel v1.35.0
 | 
			
		||||
	go.opentelemetry.io/otel/metric v1.35.0
 | 
			
		||||
	go.opentelemetry.io/otel/trace v1.35.0
 | 
			
		||||
	golang.org/x/sys v0.31.0
 | 
			
		||||
	google.golang.org/grpc v1.71.0
 | 
			
		||||
	google.golang.org/protobuf v1.36.5
 | 
			
		||||
	k8s.io/utils v0.0.0-20241210054802-24370beab758
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
@@ -22,32 +25,31 @@ require (
 | 
			
		||||
	github.com/go-logr/logr v1.4.2 // indirect
 | 
			
		||||
	github.com/go-logr/stdr v1.2.2 // indirect
 | 
			
		||||
	github.com/google/uuid v1.6.0 // indirect
 | 
			
		||||
	github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.0 // indirect
 | 
			
		||||
	github.com/klauspost/compress v1.17.11 // indirect
 | 
			
		||||
	github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.3.1 // indirect
 | 
			
		||||
	github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.3 // indirect
 | 
			
		||||
	github.com/klauspost/compress v1.18.0 // indirect
 | 
			
		||||
	github.com/mattn/go-colorable v0.1.14 // indirect
 | 
			
		||||
	github.com/mattn/go-isatty v0.0.20 // indirect
 | 
			
		||||
	github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
 | 
			
		||||
	github.com/prometheus/client_golang v1.20.5 // indirect
 | 
			
		||||
	github.com/prometheus/client_golang v1.21.1 // indirect
 | 
			
		||||
	github.com/prometheus/client_model v0.6.1 // indirect
 | 
			
		||||
	github.com/prometheus/common v0.62.0 // indirect
 | 
			
		||||
	github.com/prometheus/procfs v0.15.1 // indirect
 | 
			
		||||
	go.opentelemetry.io/auto/sdk v1.1.0 // indirect
 | 
			
		||||
	go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.59.0 // indirect
 | 
			
		||||
	go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.34.0 // indirect
 | 
			
		||||
	go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.34.0 // indirect
 | 
			
		||||
	go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.34.0 // indirect
 | 
			
		||||
	go.opentelemetry.io/otel/exporters/prometheus v0.56.0 // indirect
 | 
			
		||||
	go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.34.0 // indirect
 | 
			
		||||
	go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.34.0 // indirect
 | 
			
		||||
	go.opentelemetry.io/otel/sdk v1.34.0 // indirect
 | 
			
		||||
	go.opentelemetry.io/otel/sdk/metric v1.34.0 // indirect
 | 
			
		||||
	go.opentelemetry.io/otel/trace v1.34.0 // indirect
 | 
			
		||||
	go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.60.0 // indirect
 | 
			
		||||
	go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0 // indirect
 | 
			
		||||
	go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.35.0 // indirect
 | 
			
		||||
	go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.35.0 // indirect
 | 
			
		||||
	go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.35.0 // indirect
 | 
			
		||||
	go.opentelemetry.io/otel/exporters/prometheus v0.57.0 // indirect
 | 
			
		||||
	go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.35.0 // indirect
 | 
			
		||||
	go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.35.0 // indirect
 | 
			
		||||
	go.opentelemetry.io/otel/sdk v1.35.0 // indirect
 | 
			
		||||
	go.opentelemetry.io/otel/sdk/metric v1.35.0 // indirect
 | 
			
		||||
	go.opentelemetry.io/proto/otlp v1.5.0 // indirect
 | 
			
		||||
	golang.org/x/net v0.34.0 // indirect
 | 
			
		||||
	golang.org/x/text v0.21.0 // indirect
 | 
			
		||||
	google.golang.org/genproto/googleapis/api v0.0.0-20250127172529-29210b9bc287 // indirect
 | 
			
		||||
	google.golang.org/genproto/googleapis/rpc v0.0.0-20250127172529-29210b9bc287 // indirect
 | 
			
		||||
	google.golang.org/grpc v1.70.0 // indirect
 | 
			
		||||
	google.golang.org/protobuf v1.36.4 // indirect
 | 
			
		||||
	golang.org/x/net v0.37.0 // indirect
 | 
			
		||||
	golang.org/x/text v0.23.0 // indirect
 | 
			
		||||
	google.golang.org/genproto/googleapis/api v0.0.0-20250303144028-a0af3efb3deb // indirect
 | 
			
		||||
	google.golang.org/genproto/googleapis/rpc v0.0.0-20250303144028-a0af3efb3deb // indirect
 | 
			
		||||
	gopkg.in/yaml.v3 v3.0.1 // indirect
 | 
			
		||||
)
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										106
									
								
								go.sum
									
									
									
									
									
								
							
							
						
						
									
										106
									
								
								go.sum
									
									
									
									
									
								
							@@ -1,7 +1,9 @@
 | 
			
		||||
gitea.libretechconsulting.com/rmcguire/go-app v0.4.0 h1:iEGuA2D15rniiKlgejykxvs0TBD9JigEVnhYiCNppw4=
 | 
			
		||||
gitea.libretechconsulting.com/rmcguire/go-app v0.4.0/go.mod h1:ug6g+FyEi2LguWTQfd+bZrTd1ECsot8BylxgMFEO5DM=
 | 
			
		||||
gitea.libretechconsulting.com/rmcguire/go-app v0.4.1 h1:gjDg2M/j1AdMCtkXqQnLCo6jJUSWQOj56ehfU1S6BGE=
 | 
			
		||||
gitea.libretechconsulting.com/rmcguire/go-app v0.4.1/go.mod h1:9c71S+sJb2NqvOwt3CFsW5WjE895goiRlMTdLimgwHs=
 | 
			
		||||
gitea.libretechconsulting.com/rmcguire/go-app v0.6.0 h1:XIqk2xpKZ+GzCyh3ZpST93nu+WqteXBPCRcWVliEiks=
 | 
			
		||||
gitea.libretechconsulting.com/rmcguire/go-app v0.6.0/go.mod h1:S3/vdMEiRWWIdD0Fr+tjJc627VzxNzO4Ia2HgTBXe+g=
 | 
			
		||||
gitea.libretechconsulting.com/rmcguire/go-app v0.6.2 h1:vpEdZu7WI8qIil5NLf6OUF/Tk8+3txZ7fTv1NRRnOoc=
 | 
			
		||||
gitea.libretechconsulting.com/rmcguire/go-app v0.6.2/go.mod h1:S3/vdMEiRWWIdD0Fr+tjJc627VzxNzO4Ia2HgTBXe+g=
 | 
			
		||||
gitea.libretechconsulting.com/rmcguire/go-app v0.6.3 h1:dXYHJxK/1vmWBj1wqbqEUncFt3O92agy9gNWoa9NpA0=
 | 
			
		||||
gitea.libretechconsulting.com/rmcguire/go-app v0.6.3/go.mod h1:S3/vdMEiRWWIdD0Fr+tjJc627VzxNzO4Ia2HgTBXe+g=
 | 
			
		||||
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/caarlos0/env/v11 v11.3.1 h1:cArPWC15hWmEt+gWk7YBi7lEXTXCvpaSdCiZE2X5mCA=
 | 
			
		||||
@@ -25,16 +27,18 @@ github.com/go-resty/resty/v2 v2.16.5/go.mod h1:hkJtXbA2iKHzJheXYvQ8snQES5ZLGKMwQ
 | 
			
		||||
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
 | 
			
		||||
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
 | 
			
		||||
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
 | 
			
		||||
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
 | 
			
		||||
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
 | 
			
		||||
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
 | 
			
		||||
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
 | 
			
		||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
 | 
			
		||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
 | 
			
		||||
github.com/gorilla/schema v1.4.1 h1:jUg5hUjCSDZpNGLuXQOgIWGdlgrIdYvgQ0wZtdK1M3E=
 | 
			
		||||
github.com/gorilla/schema v1.4.1/go.mod h1:Dg5SSm5PV60mhF2NFaTV1xuYYj8tV8NOPRo4FggUMnM=
 | 
			
		||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.0 h1:VD1gqscl4nYs1YxVuSdemTrSgTKrwOWDK0FVFMqm+Cg=
 | 
			
		||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.0/go.mod h1:4EgsQoS4TOhJizV+JTFg40qx1Ofh3XmXEQNBpgvNT40=
 | 
			
		||||
github.com/klauspost/compress v1.17.11 h1:In6xLpyWOi1+C7tXUUWv2ot1QvBjxevKAaI6IXrJmUc=
 | 
			
		||||
github.com/klauspost/compress v1.17.11/go.mod h1:pMDklpSncoRMuLFrf1W9Ss9KT+0rH90U12bZKk7uwG0=
 | 
			
		||||
github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.3.1 h1:KcFzXwzM/kGhIRHvc8jdixfIJjVzuUJdnv+5xsPutog=
 | 
			
		||||
github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.3.1/go.mod h1:qOchhhIlmRcqk/O9uCo/puJlyo07YINaIqdZfZG3Jkc=
 | 
			
		||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.3 h1:5ZPtiqj0JL5oKWmcsq4VMaAW5ukBEgSGXEN89zeH1Jo=
 | 
			
		||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.3/go.mod h1:ndYquD05frm2vACXE1nsccT4oJzjhw2arTS2cpUD1PI=
 | 
			
		||||
github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo=
 | 
			
		||||
github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ=
 | 
			
		||||
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
 | 
			
		||||
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
 | 
			
		||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
 | 
			
		||||
@@ -53,8 +57,8 @@ github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8m
 | 
			
		||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
 | 
			
		||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
 | 
			
		||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
 | 
			
		||||
github.com/prometheus/client_golang v1.20.5 h1:cxppBPuYhUnsO6yo/aoRol4L7q7UFfdm+bR9r+8l63Y=
 | 
			
		||||
github.com/prometheus/client_golang v1.20.5/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE=
 | 
			
		||||
github.com/prometheus/client_golang v1.21.1 h1:DOvXXTqVzvkIewV/CDPFdejpMCGeMcbGCQ8YOmu+Ibk=
 | 
			
		||||
github.com/prometheus/client_golang v1.21.1/go.mod h1:U9NM32ykUErtVBxdvD3zfi+EuFkkaBvMb09mIfe0Zgg=
 | 
			
		||||
github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E=
 | 
			
		||||
github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY=
 | 
			
		||||
github.com/prometheus/common v0.62.0 h1:xasJaQlnWAeyHdUBeGjXmutelfJHWMRr+Fg4QszZ2Io=
 | 
			
		||||
@@ -70,53 +74,55 @@ github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOf
 | 
			
		||||
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
 | 
			
		||||
go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA=
 | 
			
		||||
go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A=
 | 
			
		||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.59.0 h1:CV7UdSGJt/Ao6Gp4CXckLxVRRsRgDHoI8XjbL3PDl8s=
 | 
			
		||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.59.0/go.mod h1:FRmFuRJfag1IZ2dPkHnEoSFVgTVPUd2qf5Vi69hLb8I=
 | 
			
		||||
go.opentelemetry.io/otel v1.34.0 h1:zRLXxLCgL1WyKsPVrgbSdMN4c0FMkDAskSTQP+0hdUY=
 | 
			
		||||
go.opentelemetry.io/otel v1.34.0/go.mod h1:OWFPOQ+h4G8xpyjgqo4SxJYdDQ/qmRH+wivy7zzx9oI=
 | 
			
		||||
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.34.0 h1:ajl4QczuJVA2TU9W9AGw++86Xga/RKt//16z/yxPgdk=
 | 
			
		||||
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.34.0/go.mod h1:Vn3/rlOJ3ntf/Q3zAI0V5lDnTbHGaUsNUeF6nZmm7pA=
 | 
			
		||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.34.0 h1:OeNbIYk/2C15ckl7glBlOBp5+WlYsOElzTNmiPW/x60=
 | 
			
		||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.34.0/go.mod h1:7Bept48yIeqxP2OZ9/AqIpYS94h2or0aB4FypJTc8ZM=
 | 
			
		||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.34.0 h1:tgJ0uaNS4c98WRNUEx5U3aDlrDOI5Rs+1Vifcw4DJ8U=
 | 
			
		||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.34.0/go.mod h1:U7HYyW0zt/a9x5J1Kjs+r1f/d4ZHnYFclhYY2+YbeoE=
 | 
			
		||||
go.opentelemetry.io/otel/exporters/prometheus v0.56.0 h1:GnCIi0QyG0yy2MrJLzVrIM7laaJstj//flf1zEJCG+E=
 | 
			
		||||
go.opentelemetry.io/otel/exporters/prometheus v0.56.0/go.mod h1:JQcVZtbIIPM+7SWBB+T6FK+xunlyidwLp++fN0sUaOk=
 | 
			
		||||
go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.34.0 h1:czJDQwFrMbOr9Kk+BPo1y8WZIIFIK58SA1kykuVeiOU=
 | 
			
		||||
go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.34.0/go.mod h1:lT7bmsxOe58Tq+JIOkTQMCGXdu47oA+VJKLZHbaBKbs=
 | 
			
		||||
go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.34.0 h1:jBpDk4HAUsrnVO1FsfCfCOTEc/MkInJmvfCHYLFiT80=
 | 
			
		||||
go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.34.0/go.mod h1:H9LUIM1daaeZaz91vZcfeM0fejXPmgCYE8ZhzqfJuiU=
 | 
			
		||||
go.opentelemetry.io/otel/metric v1.34.0 h1:+eTR3U0MyfWjRDhmFMxe2SsW64QrZ84AOhvqS7Y+PoQ=
 | 
			
		||||
go.opentelemetry.io/otel/metric v1.34.0/go.mod h1:CEDrp0fy2D0MvkXE+dPV7cMi8tWZwX3dmaIhwPOaqHE=
 | 
			
		||||
go.opentelemetry.io/otel/sdk v1.34.0 h1:95zS4k/2GOy069d321O8jWgYsW3MzVV+KuSPKp7Wr1A=
 | 
			
		||||
go.opentelemetry.io/otel/sdk v1.34.0/go.mod h1:0e/pNiaMAqaykJGKbi+tSjWfNNHMTxoC9qANsCzbyxU=
 | 
			
		||||
go.opentelemetry.io/otel/sdk/metric v1.34.0 h1:5CeK9ujjbFVL5c1PhLuStg1wxA7vQv7ce1EK0Gyvahk=
 | 
			
		||||
go.opentelemetry.io/otel/sdk/metric v1.34.0/go.mod h1:jQ/r8Ze28zRKoNRdkjCZxfs6YvBTG1+YIqyFVFYec5w=
 | 
			
		||||
go.opentelemetry.io/otel/trace v1.34.0 h1:+ouXS2V8Rd4hp4580a8q23bg0azF2nI8cqLYnC8mh/k=
 | 
			
		||||
go.opentelemetry.io/otel/trace v1.34.0/go.mod h1:Svm7lSjQD7kG7KJ/MUHPVXSDGz2OX4h0M2jHBhmSfRE=
 | 
			
		||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.60.0 h1:x7wzEgXfnzJcHDwStJT+mxOz4etr2EcexjqhBvmoakw=
 | 
			
		||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.60.0/go.mod h1:rg+RlpR5dKwaS95IyyZqj5Wd4E13lk/msnTS0Xl9lJM=
 | 
			
		||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0 h1:sbiXRNDSWJOTobXh5HyQKjq6wUC5tNybqjIqDpAY4CU=
 | 
			
		||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0/go.mod h1:69uWxva0WgAA/4bu2Yy70SLDBwZXuQ6PbBpbsa5iZrQ=
 | 
			
		||||
go.opentelemetry.io/otel v1.35.0 h1:xKWKPxrxB6OtMCbmMY021CqC45J+3Onta9MqjhnusiQ=
 | 
			
		||||
go.opentelemetry.io/otel v1.35.0/go.mod h1:UEqy8Zp11hpkUrL73gSlELM0DupHoiq72dR+Zqel/+Y=
 | 
			
		||||
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.35.0 h1:QcFwRrZLc82r8wODjvyCbP7Ifp3UANaBSmhDSFjnqSc=
 | 
			
		||||
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.35.0/go.mod h1:CXIWhUomyWBG/oY2/r/kLp6K/cmx9e/7DLpBuuGdLCA=
 | 
			
		||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.35.0 h1:1fTNlAIJZGWLP5FVu0fikVry1IsiUnXjf7QFvoNN3Xw=
 | 
			
		||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.35.0/go.mod h1:zjPK58DtkqQFn+YUMbx0M2XV3QgKU0gS9LeGohREyK4=
 | 
			
		||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.35.0 h1:m639+BofXTvcY1q8CGs4ItwQarYtJPOWmVobfM1HpVI=
 | 
			
		||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.35.0/go.mod h1:LjReUci/F4BUyv+y4dwnq3h/26iNOeC3wAIqgvTIZVo=
 | 
			
		||||
go.opentelemetry.io/otel/exporters/prometheus v0.57.0 h1:AHh/lAP1BHrY5gBwk8ncc25FXWm/gmmY3BX258z5nuk=
 | 
			
		||||
go.opentelemetry.io/otel/exporters/prometheus v0.57.0/go.mod h1:QpFWz1QxqevfjwzYdbMb4Y1NnlJvqSGwyuU0B4iuc9c=
 | 
			
		||||
go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.35.0 h1:PB3Zrjs1sG1GBX51SXyTSoOTqcDglmsk7nT6tkKPb/k=
 | 
			
		||||
go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.35.0/go.mod h1:U2R3XyVPzn0WX7wOIypPuptulsMcPDPs/oiSVOMVnHY=
 | 
			
		||||
go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.35.0 h1:T0Ec2E+3YZf5bgTNQVet8iTDW7oIk03tXHq+wkwIDnE=
 | 
			
		||||
go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.35.0/go.mod h1:30v2gqH+vYGJsesLWFov8u47EpYTcIQcBjKpI6pJThg=
 | 
			
		||||
go.opentelemetry.io/otel/metric v1.35.0 h1:0znxYu2SNyuMSQT4Y9WDWej0VpcsxkuklLa4/siN90M=
 | 
			
		||||
go.opentelemetry.io/otel/metric v1.35.0/go.mod h1:nKVFgxBZ2fReX6IlyW28MgZojkoAkJGaE8CpgeAU3oE=
 | 
			
		||||
go.opentelemetry.io/otel/sdk v1.35.0 h1:iPctf8iprVySXSKJffSS79eOjl9pvxV9ZqOWT0QejKY=
 | 
			
		||||
go.opentelemetry.io/otel/sdk v1.35.0/go.mod h1:+ga1bZliga3DxJ3CQGg3updiaAJoNECOgJREo9KHGQg=
 | 
			
		||||
go.opentelemetry.io/otel/sdk/metric v1.35.0 h1:1RriWBmCKgkeHEhM7a2uMjMUfP7MsOF5JpUCaEqEI9o=
 | 
			
		||||
go.opentelemetry.io/otel/sdk/metric v1.35.0/go.mod h1:is6XYCUMpcKi+ZsOvfluY5YstFnhW0BidkR+gL+qN+w=
 | 
			
		||||
go.opentelemetry.io/otel/trace v1.35.0 h1:dPpEfJu1sDIqruz7BHFG3c7528f6ddfSWfFDVt/xgMs=
 | 
			
		||||
go.opentelemetry.io/otel/trace v1.35.0/go.mod h1:WUk7DtFp1Aw2MkvqGdwiXYDZZNvA/1J8o6xRXLrIkyc=
 | 
			
		||||
go.opentelemetry.io/proto/otlp v1.5.0 h1:xJvq7gMzB31/d406fB8U5CBdyQGw4P399D1aQWU/3i4=
 | 
			
		||||
go.opentelemetry.io/proto/otlp v1.5.0/go.mod h1:keN8WnHxOy8PG0rQZjJJ5A2ebUoafqWp0eVQ4yIXvJ4=
 | 
			
		||||
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
 | 
			
		||||
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
 | 
			
		||||
golang.org/x/net v0.34.0 h1:Mb7Mrk043xzHgnRM88suvJFwzVrRfHEHJEl5/71CKw0=
 | 
			
		||||
golang.org/x/net v0.34.0/go.mod h1:di0qlW3YNM5oh6GqDGQr92MyTozJPmybPK4Ev/Gm31k=
 | 
			
		||||
golang.org/x/net v0.37.0 h1:1zLorHbz+LYj7MQlSf1+2tPIIgibq2eL5xkrGk6f+2c=
 | 
			
		||||
golang.org/x/net v0.37.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8=
 | 
			
		||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 | 
			
		||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 | 
			
		||||
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 | 
			
		||||
golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU=
 | 
			
		||||
golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
 | 
			
		||||
golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo=
 | 
			
		||||
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
 | 
			
		||||
golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik=
 | 
			
		||||
golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
 | 
			
		||||
golang.org/x/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY=
 | 
			
		||||
golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4=
 | 
			
		||||
golang.org/x/time v0.6.0 h1:eTDhh4ZXt5Qf0augr54TN6suAUudPcawVZeIAPU7D4U=
 | 
			
		||||
golang.org/x/time v0.6.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
 | 
			
		||||
google.golang.org/genproto/googleapis/api v0.0.0-20250127172529-29210b9bc287 h1:A2ni10G3UlplFrWdCDJTl7D7mJ7GSRm37S+PDimaKRw=
 | 
			
		||||
google.golang.org/genproto/googleapis/api v0.0.0-20250127172529-29210b9bc287/go.mod h1:iYONQfRdizDB8JJBybql13nArx91jcUk7zCXEsOofM4=
 | 
			
		||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250127172529-29210b9bc287 h1:J1H9f+LEdWAfHcez/4cvaVBox7cOYT+IU6rgqj5x++8=
 | 
			
		||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250127172529-29210b9bc287/go.mod h1:8BS3B93F/U1juMFq9+EDk+qOT5CO1R9IzXxG3PTqiRk=
 | 
			
		||||
google.golang.org/grpc v1.70.0 h1:pWFv03aZoHzlRKHWicjsZytKAiYCtNS0dHbXnIdq7jQ=
 | 
			
		||||
google.golang.org/grpc v1.70.0/go.mod h1:ofIJqVKDXx/JiXrwr2IG4/zwdH9txy3IlF40RmcJSQw=
 | 
			
		||||
google.golang.org/protobuf v1.36.4 h1:6A3ZDJHn/eNqc1i+IdefRzy/9PokBTPvcqMySR7NNIM=
 | 
			
		||||
google.golang.org/protobuf v1.36.4/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
 | 
			
		||||
google.golang.org/genproto/googleapis/api v0.0.0-20250303144028-a0af3efb3deb h1:p31xT4yrYrSM/G4Sn2+TNUkVhFCbG9y8itM2S6Th950=
 | 
			
		||||
google.golang.org/genproto/googleapis/api v0.0.0-20250303144028-a0af3efb3deb/go.mod h1:jbe3Bkdp+Dh2IrslsFCklNhweNTBgSYanP1UXhJDhKg=
 | 
			
		||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250303144028-a0af3efb3deb h1:TLPQVbx1GJ8VKZxz52VAxl1EBgKXXbTiU9Fc5fZeLn4=
 | 
			
		||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250303144028-a0af3efb3deb/go.mod h1:LuRYeWDFV6WOn90g357N17oMCaxpgCnbi/44qJvDn2I=
 | 
			
		||||
google.golang.org/grpc v1.71.0 h1:kF77BGdPTQ4/JZWMlb9VpJ5pa25aqvVqogsxNHHdeBg=
 | 
			
		||||
google.golang.org/grpc v1.71.0/go.mod h1:H0GRtasmQOh9LkFoCPDu3ZrwUtD1YGE+b2vYBYd/8Ec=
 | 
			
		||||
google.golang.org/protobuf v1.36.5 h1:tPhr+woSbjfYvY6/GPufUoYizxw1cF/yFoxJ2fmpwlM=
 | 
			
		||||
google.golang.org/protobuf v1.36.5/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
 | 
			
		||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 | 
			
		||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
 | 
			
		||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										23
									
								
								helm/ambient-local-exporter/.helmignore
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								helm/ambient-local-exporter/.helmignore
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,23 @@
 | 
			
		||||
# Patterns to ignore when building packages.
 | 
			
		||||
# This supports shell glob matching, relative path matching, and
 | 
			
		||||
# negation (prefixed with !). Only one pattern per line.
 | 
			
		||||
.DS_Store
 | 
			
		||||
# Common VCS dirs
 | 
			
		||||
.git/
 | 
			
		||||
.gitignore
 | 
			
		||||
.bzr/
 | 
			
		||||
.bzrignore
 | 
			
		||||
.hg/
 | 
			
		||||
.hgignore
 | 
			
		||||
.svn/
 | 
			
		||||
# Common backup files
 | 
			
		||||
*.swp
 | 
			
		||||
*.bak
 | 
			
		||||
*.tmp
 | 
			
		||||
*.orig
 | 
			
		||||
*~
 | 
			
		||||
# Various IDEs
 | 
			
		||||
.project
 | 
			
		||||
.idea/
 | 
			
		||||
*.tmproj
 | 
			
		||||
.vscode/
 | 
			
		||||
							
								
								
									
										6
									
								
								helm/ambient-local-exporter/Chart.lock
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								helm/ambient-local-exporter/Chart.lock
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,6 @@
 | 
			
		||||
dependencies:
 | 
			
		||||
- name: hull
 | 
			
		||||
  repository: https://vidispine.github.io/hull
 | 
			
		||||
  version: 1.32.2
 | 
			
		||||
digest: sha256:7b73a7f152916fed9842efe4f65081b1cda0fcebd8f36d27e48136b608ce305f
 | 
			
		||||
generated: "2025-03-08T12:22:41.343082-05:00"
 | 
			
		||||
							
								
								
									
										30
									
								
								helm/ambient-local-exporter/Chart.yaml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								helm/ambient-local-exporter/Chart.yaml
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,30 @@
 | 
			
		||||
apiVersion: v2
 | 
			
		||||
name: ambient-local-exporter
 | 
			
		||||
description: Cloud-free metrics exporter for ambient weather stations
 | 
			
		||||
 | 
			
		||||
# A chart can be either an 'application' or a 'library' chart.
 | 
			
		||||
#
 | 
			
		||||
# Application charts are a collection of templates that can be packaged into versioned archives
 | 
			
		||||
# to be deployed.
 | 
			
		||||
#
 | 
			
		||||
# Library charts provide useful utilities or functions for the chart developer. They're included as
 | 
			
		||||
# a dependency of application charts to inject those utilities and functions into the rendering
 | 
			
		||||
# pipeline. Library charts do not define any templates and therefore cannot be deployed.
 | 
			
		||||
type: application
 | 
			
		||||
 | 
			
		||||
# This is the chart version. This version number should be incremented each time you make changes
 | 
			
		||||
# to the chart and its templates, including the app version.
 | 
			
		||||
# Versions are expected to follow Semantic Versioning (https://semver.org/)
 | 
			
		||||
version: 0.1.0
 | 
			
		||||
 | 
			
		||||
# This is the version number of the application being deployed. This version number should be
 | 
			
		||||
# incremented each time you make changes to the application. Versions are not expected to
 | 
			
		||||
# follow Semantic Versioning. They should reflect the version the application is using.
 | 
			
		||||
# It is recommended to use it with quotes.
 | 
			
		||||
appVersion: "0.9.0"
 | 
			
		||||
 | 
			
		||||
dependencies:
 | 
			
		||||
  - name: hull
 | 
			
		||||
    repository: https://vidispine.github.io/hull
 | 
			
		||||
    version: 1.32.2
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										
											BIN
										
									
								
								helm/ambient-local-exporter/charts/hull-1.32.2.tgz
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								helm/ambient-local-exporter/charts/hull-1.32.2.tgz
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										643
									
								
								helm/ambient-local-exporter/library-hull.yaml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										643
									
								
								helm/ambient-local-exporter/library-hull.yaml
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,643 @@
 | 
			
		||||
################################ 
 | 
			
		||||
### values.yaml for HULL     
 | 
			
		||||
### The basic pre-configuration takes place here.
 | 
			
		||||
###
 | 
			
		||||
### Do not change this file, use additional values.hull.yaml 
 | 
			
		||||
### to overwrite the selected fields!
 | 
			
		||||
################################
 | 
			
		||||
 | 
			
		||||
###################################################
 | 
			
		||||
### CONFIG
 | 
			
		||||
config:
 | 
			
		||||
  general:  
 | 
			
		||||
    rbac: true
 | 
			
		||||
    fullnameOverride: ""
 | 
			
		||||
    nameOverride: ""
 | 
			
		||||
    namespaceOverride: ""
 | 
			
		||||
    noObjectNamePrefixes: false
 | 
			
		||||
    createImagePullSecretsFromRegistries: true
 | 
			
		||||
    globalImageRegistryServer: ""
 | 
			
		||||
    globalImageRegistryToFirstRegistrySecretServer: false
 | 
			
		||||
    serialization:
 | 
			
		||||
      configmap:
 | 
			
		||||
        enabled: true
 | 
			
		||||
        fileExtensions:
 | 
			
		||||
          json: toPrettyJson
 | 
			
		||||
          yml: toYaml
 | 
			
		||||
          yaml: toYaml
 | 
			
		||||
      secret:
 | 
			
		||||
        enabled: true
 | 
			
		||||
        fileExtensions:
 | 
			
		||||
          json: toPrettyJson
 | 
			
		||||
          yml: toYaml
 | 
			
		||||
          yaml: toYaml
 | 
			
		||||
    render:
 | 
			
		||||
      passes: 3
 | 
			
		||||
      emptyLabels: false
 | 
			
		||||
      emptyAnnotations: false
 | 
			
		||||
      emptyTemplateLabels: false
 | 
			
		||||
      emptyTemplateAnnotations: false
 | 
			
		||||
      emptyHullObjects: false
 | 
			
		||||
    postRender:
 | 
			
		||||
      globalStringReplacements:
 | 
			
		||||
        instanceKey:
 | 
			
		||||
          enabled: false
 | 
			
		||||
          string: _HULL_OBJECT_TYPE_DEFAULT_
 | 
			
		||||
          replacement: OBJECT_INSTANCE_KEY
 | 
			
		||||
        instanceKeyResolved:
 | 
			
		||||
          enabled: false
 | 
			
		||||
          string: _HULL_OBJECT_TYPE_DEFAULT_
 | 
			
		||||
          replacement: OBJECT_INSTANCE_KEY_RESOLVED
 | 
			
		||||
        instanceName:
 | 
			
		||||
          enabled: false
 | 
			
		||||
          string: _HULL_OBJECT_TYPE_DEFAULT_
 | 
			
		||||
          replacement: OBJECT_INSTANCE_NAME
 | 
			
		||||
    errorChecks:
 | 
			
		||||
      objectYamlValid: true
 | 
			
		||||
      hullGetTransformationReferenceValid: true
 | 
			
		||||
      containerImageValid: true
 | 
			
		||||
      virtualFolderDataPathExists: true
 | 
			
		||||
      virtualFolderDataInlineValid: false
 | 
			
		||||
    debug:
 | 
			
		||||
      renderBrokenHullGetTransformationReferences: false
 | 
			
		||||
      renderNilWhenInlineIsNil: false
 | 
			
		||||
      renderPathMissingWhenPathIsNonExistent: false
 | 
			
		||||
    metadata:
 | 
			
		||||
      labels:
 | 
			
		||||
        common:
 | 
			
		||||
          'app.kubernetes.io/managed-by':
 | 
			
		||||
          'app.kubernetes.io/version':
 | 
			
		||||
          'app.kubernetes.io/part-of':
 | 
			
		||||
          'app.kubernetes.io/name': 
 | 
			
		||||
          'app.kubernetes.io/instance':
 | 
			
		||||
          'app.kubernetes.io/component':
 | 
			
		||||
          'helm.sh/chart':
 | 
			
		||||
          'vidispine.hull/version':
 | 
			
		||||
        custom: {}
 | 
			
		||||
      annotations: 
 | 
			
		||||
        hashes: false
 | 
			
		||||
        custom: {}
 | 
			
		||||
    data: {} 
 | 
			
		||||
  specific: {}
 | 
			
		||||
  templates: 
 | 
			
		||||
    pod: 
 | 
			
		||||
      global: {}
 | 
			
		||||
    container:
 | 
			
		||||
      global: {}
 | 
			
		||||
###################################################
 | 
			
		||||
        
 | 
			
		||||
###################################################
 | 
			
		||||
### OBJECTS
 | 
			
		||||
objects:
 | 
			
		||||
 | 
			
		||||
# NAMESPACE
 | 
			
		||||
  namespace:
 | 
			
		||||
    _HULL_OBJECT_TYPE_DEFAULT_:
 | 
			
		||||
      enabled: true
 | 
			
		||||
      staticName: true
 | 
			
		||||
      annotations: {}
 | 
			
		||||
      labels: {}
 | 
			
		||||
###################################################
 | 
			
		||||
 | 
			
		||||
# CONFIGMAPS
 | 
			
		||||
  configmap:
 | 
			
		||||
    _HULL_OBJECT_TYPE_DEFAULT_:
 | 
			
		||||
      enabled: true
 | 
			
		||||
      annotations: {}
 | 
			
		||||
      labels: {}
 | 
			
		||||
###################################################
 | 
			
		||||
 | 
			
		||||
# SECRETS
 | 
			
		||||
  secret:
 | 
			
		||||
    _HULL_OBJECT_TYPE_DEFAULT_:
 | 
			
		||||
      enabled: true
 | 
			
		||||
      annotations: {}
 | 
			
		||||
      labels: {}
 | 
			
		||||
###################################################
 | 
			
		||||
 | 
			
		||||
# REGISTRIES
 | 
			
		||||
  registry:
 | 
			
		||||
    _HULL_OBJECT_TYPE_DEFAULT_:
 | 
			
		||||
      enabled: true
 | 
			
		||||
      annotations: {}
 | 
			
		||||
      labels: {}
 | 
			
		||||
###################################################
 | 
			
		||||
 | 
			
		||||
# SERVICEACCOUNTS
 | 
			
		||||
  serviceaccount:
 | 
			
		||||
    _HULL_OBJECT_TYPE_DEFAULT_:
 | 
			
		||||
      enabled: true 
 | 
			
		||||
      annotations: {}
 | 
			
		||||
      labels: {}
 | 
			
		||||
    default:
 | 
			
		||||
      enabled: _HT?eq (dig "serviceAccountName" "" _HT*hull.config.templates.pod.global) ""
 | 
			
		||||
      annotations: {}
 | 
			
		||||
      labels: {}
 | 
			
		||||
###################################################
 | 
			
		||||
 | 
			
		||||
# ROLES
 | 
			
		||||
  role:
 | 
			
		||||
    _HULL_OBJECT_TYPE_DEFAULT_:
 | 
			
		||||
      enabled: true
 | 
			
		||||
      annotations: {}
 | 
			
		||||
      labels: {}
 | 
			
		||||
      rules:
 | 
			
		||||
        _HULL_OBJECT_TYPE_DEFAULT_: {}
 | 
			
		||||
    default:
 | 
			
		||||
      enabled: _HT?eq (dig "serviceAccountName" "" _HT*hull.config.templates.pod.global) ""
 | 
			
		||||
      rules: {}
 | 
			
		||||
###################################################
 | 
			
		||||
 | 
			
		||||
# ROLEBINDINGS
 | 
			
		||||
  rolebinding:
 | 
			
		||||
    _HULL_OBJECT_TYPE_DEFAULT_:
 | 
			
		||||
      enabled: true
 | 
			
		||||
      annotations: {}
 | 
			
		||||
      labels: {}
 | 
			
		||||
    default:
 | 
			
		||||
      enabled: _HT?eq (dig "serviceAccountName" "" _HT*hull.config.templates.pod.global) ""
 | 
			
		||||
      roleRef:
 | 
			
		||||
        apiGroup: "rbac.authorization.k8s.io"
 | 
			
		||||
        kind: "Role"
 | 
			
		||||
        name: _HT^default
 | 
			
		||||
      subjects: 
 | 
			
		||||
      - kind: ServiceAccount
 | 
			
		||||
        name:  _HT^default
 | 
			
		||||
        namespace: _HT**Release.Namespace
 | 
			
		||||
###################################################
 | 
			
		||||
 | 
			
		||||
# CLUSTERROLES
 | 
			
		||||
  clusterrole:
 | 
			
		||||
    _HULL_OBJECT_TYPE_DEFAULT_:
 | 
			
		||||
      enabled: true
 | 
			
		||||
      annotations: {}
 | 
			
		||||
      labels: {}
 | 
			
		||||
      rules:
 | 
			
		||||
        _HULL_OBJECT_TYPE_DEFAULT_: {}
 | 
			
		||||
###################################################
 | 
			
		||||
 | 
			
		||||
# CLUSTERROLEBINDINGS
 | 
			
		||||
  clusterrolebinding:
 | 
			
		||||
    _HULL_OBJECT_TYPE_DEFAULT_:
 | 
			
		||||
      enabled: true
 | 
			
		||||
      annotations: {}
 | 
			
		||||
      labels: {}   
 | 
			
		||||
###################################################
 | 
			
		||||
 | 
			
		||||
# CUSTOMRESOURCEDEFINITIONS (deprecated with Helm3)
 | 
			
		||||
#  customresourcedefinitions:
 | 
			
		||||
#    _HULL_OBJECT_TYPE_DEFAULT_:
 | 
			
		||||
#      enabled: true
 | 
			
		||||
#      annotations: {}
 | 
			
		||||
#      labels: {}
 | 
			
		||||
###################################################
 | 
			
		||||
 | 
			
		||||
# CUSTOMRESOURCES 
 | 
			
		||||
  customresource:
 | 
			
		||||
    _HULL_OBJECT_TYPE_DEFAULT_:
 | 
			
		||||
      enabled: true
 | 
			
		||||
      annotations: {}
 | 
			
		||||
      labels: {}
 | 
			
		||||
###################################################
 | 
			
		||||
 | 
			
		||||
# PERSISTENTVOLUMECLAIMS 
 | 
			
		||||
  persistentvolumeclaim:
 | 
			
		||||
    _HULL_OBJECT_TYPE_DEFAULT_:
 | 
			
		||||
      enabled: true
 | 
			
		||||
      annotations: {}
 | 
			
		||||
      labels: {}
 | 
			
		||||
###################################################
 | 
			
		||||
 | 
			
		||||
# PERSISTENTVOLUMES 
 | 
			
		||||
  persistentvolume:
 | 
			
		||||
    _HULL_OBJECT_TYPE_DEFAULT_:
 | 
			
		||||
      enabled: true
 | 
			
		||||
      annotations: {}
 | 
			
		||||
      labels: {}   
 | 
			
		||||
###################################################
 | 
			
		||||
 | 
			
		||||
# STORAGECLASSES 
 | 
			
		||||
  storageclass:
 | 
			
		||||
    _HULL_OBJECT_TYPE_DEFAULT_:
 | 
			
		||||
      enabled: true
 | 
			
		||||
      annotations: {}
 | 
			
		||||
      labels: {}
 | 
			
		||||
###################################################
 | 
			
		||||
 | 
			
		||||
# SERVICES 
 | 
			
		||||
  service:
 | 
			
		||||
    _HULL_OBJECT_TYPE_DEFAULT_:
 | 
			
		||||
      enabled: true
 | 
			
		||||
      annotations: {}
 | 
			
		||||
      labels: {}
 | 
			
		||||
      ports:
 | 
			
		||||
        _HULL_OBJECT_TYPE_DEFAULT_: {}
 | 
			
		||||
 | 
			
		||||
###################################################
 | 
			
		||||
 | 
			
		||||
# INGRESSES 
 | 
			
		||||
  ingress:
 | 
			
		||||
    _HULL_OBJECT_TYPE_DEFAULT_:
 | 
			
		||||
      enabled: true
 | 
			
		||||
      annotations: {}
 | 
			
		||||
      labels: {}
 | 
			
		||||
      tls:
 | 
			
		||||
        _HULL_OBJECT_TYPE_DEFAULT_: {}
 | 
			
		||||
      rules:
 | 
			
		||||
        _HULL_OBJECT_TYPE_DEFAULT_:
 | 
			
		||||
          http:
 | 
			
		||||
            paths:
 | 
			
		||||
              _HULL_OBJECT_TYPE_DEFAULT_: {}
 | 
			
		||||
 | 
			
		||||
###################################################
 | 
			
		||||
 | 
			
		||||
# INGRESSCLASSES 
 | 
			
		||||
  ingressclass:
 | 
			
		||||
    _HULL_OBJECT_TYPE_DEFAULT_:
 | 
			
		||||
      enabled: true
 | 
			
		||||
      annotations: {}
 | 
			
		||||
      labels: {}
 | 
			
		||||
 | 
			
		||||
###################################################
 | 
			
		||||
 | 
			
		||||
# DEPLOYMENTS 
 | 
			
		||||
  deployment:
 | 
			
		||||
    _HULL_OBJECT_TYPE_DEFAULT_:
 | 
			
		||||
      enabled: true
 | 
			
		||||
      annotations: {}
 | 
			
		||||
      labels: {}
 | 
			
		||||
      templateAnnotations: {}
 | 
			
		||||
      templateLabels: {}
 | 
			
		||||
      pod:        
 | 
			
		||||
        initContainers: 
 | 
			
		||||
          _HULL_OBJECT_TYPE_DEFAULT_:
 | 
			
		||||
            env: 
 | 
			
		||||
              _HULL_OBJECT_TYPE_DEFAULT_: {}
 | 
			
		||||
            envFrom:
 | 
			
		||||
              _HULL_OBJECT_TYPE_DEFAULT_: {}
 | 
			
		||||
            volumeMounts:
 | 
			
		||||
              _HULL_OBJECT_TYPE_DEFAULT_: {}
 | 
			
		||||
        containers: 
 | 
			
		||||
          _HULL_OBJECT_TYPE_DEFAULT_:
 | 
			
		||||
            env: 
 | 
			
		||||
              _HULL_OBJECT_TYPE_DEFAULT_: {}
 | 
			
		||||
            envFrom:
 | 
			
		||||
              _HULL_OBJECT_TYPE_DEFAULT_: {}
 | 
			
		||||
            volumeMounts:
 | 
			
		||||
              _HULL_OBJECT_TYPE_DEFAULT_: {}
 | 
			
		||||
        volumes: 
 | 
			
		||||
          _HULL_OBJECT_TYPE_DEFAULT_: {}
 | 
			
		||||
###################################################
 | 
			
		||||
 | 
			
		||||
# JOBS 
 | 
			
		||||
  job:
 | 
			
		||||
    _HULL_OBJECT_TYPE_DEFAULT_:
 | 
			
		||||
      enabled: true
 | 
			
		||||
      annotations: {}
 | 
			
		||||
      labels: {}
 | 
			
		||||
      templateAnnotations: {}
 | 
			
		||||
      templateLabels: {}
 | 
			
		||||
      pod:
 | 
			
		||||
        initContainers: 
 | 
			
		||||
          _HULL_OBJECT_TYPE_DEFAULT_:
 | 
			
		||||
            env: 
 | 
			
		||||
              _HULL_OBJECT_TYPE_DEFAULT_: {}
 | 
			
		||||
            envFrom:
 | 
			
		||||
              _HULL_OBJECT_TYPE_DEFAULT_: {}
 | 
			
		||||
            volumeMounts:
 | 
			
		||||
              _HULL_OBJECT_TYPE_DEFAULT_: {}
 | 
			
		||||
        containers: 
 | 
			
		||||
          _HULL_OBJECT_TYPE_DEFAULT_:
 | 
			
		||||
            env: 
 | 
			
		||||
              _HULL_OBJECT_TYPE_DEFAULT_: {}
 | 
			
		||||
            envFrom:
 | 
			
		||||
              _HULL_OBJECT_TYPE_DEFAULT_: {}
 | 
			
		||||
            volumeMounts:
 | 
			
		||||
              _HULL_OBJECT_TYPE_DEFAULT_: {}
 | 
			
		||||
        volumes: 
 | 
			
		||||
          _HULL_OBJECT_TYPE_DEFAULT_: {}
 | 
			
		||||
###################################################
 | 
			
		||||
 | 
			
		||||
# CRONJOBS 
 | 
			
		||||
  cronjob:
 | 
			
		||||
    _HULL_OBJECT_TYPE_DEFAULT_:
 | 
			
		||||
      enabled: true
 | 
			
		||||
      annotations: {}
 | 
			
		||||
      labels: {}
 | 
			
		||||
      job:
 | 
			
		||||
        templateAnnotations: {}
 | 
			
		||||
        templateLabels: {}
 | 
			
		||||
        pod:
 | 
			
		||||
          initContainers: 
 | 
			
		||||
            _HULL_OBJECT_TYPE_DEFAULT_:
 | 
			
		||||
              env: 
 | 
			
		||||
                _HULL_OBJECT_TYPE_DEFAULT_: {}
 | 
			
		||||
              envFrom:
 | 
			
		||||
                _HULL_OBJECT_TYPE_DEFAULT_: {}
 | 
			
		||||
              volumeMounts:
 | 
			
		||||
                _HULL_OBJECT_TYPE_DEFAULT_: {}
 | 
			
		||||
          containers: 
 | 
			
		||||
            _HULL_OBJECT_TYPE_DEFAULT_:
 | 
			
		||||
              env: 
 | 
			
		||||
                _HULL_OBJECT_TYPE_DEFAULT_: {}
 | 
			
		||||
              envFrom:
 | 
			
		||||
                _HULL_OBJECT_TYPE_DEFAULT_: {}
 | 
			
		||||
              volumeMounts:
 | 
			
		||||
                _HULL_OBJECT_TYPE_DEFAULT_: {}
 | 
			
		||||
          volumes: 
 | 
			
		||||
            _HULL_OBJECT_TYPE_DEFAULT_: {}
 | 
			
		||||
###################################################
 | 
			
		||||
 | 
			
		||||
# DAEMONSETS 
 | 
			
		||||
  daemonset:
 | 
			
		||||
    _HULL_OBJECT_TYPE_DEFAULT_:
 | 
			
		||||
      enabled: true
 | 
			
		||||
      annotations: {}
 | 
			
		||||
      labels: {}
 | 
			
		||||
      templateAnnotations: {}
 | 
			
		||||
      templateLabels: {}
 | 
			
		||||
      pod:
 | 
			
		||||
        initContainers: 
 | 
			
		||||
          _HULL_OBJECT_TYPE_DEFAULT_:
 | 
			
		||||
            env: 
 | 
			
		||||
              _HULL_OBJECT_TYPE_DEFAULT_: {}
 | 
			
		||||
            envFrom:
 | 
			
		||||
              _HULL_OBJECT_TYPE_DEFAULT_: {}
 | 
			
		||||
            volumeMounts:
 | 
			
		||||
              _HULL_OBJECT_TYPE_DEFAULT_: {}
 | 
			
		||||
        containers: 
 | 
			
		||||
          _HULL_OBJECT_TYPE_DEFAULT_:
 | 
			
		||||
            env: 
 | 
			
		||||
              _HULL_OBJECT_TYPE_DEFAULT_: {}
 | 
			
		||||
            envFrom:
 | 
			
		||||
              _HULL_OBJECT_TYPE_DEFAULT_: {}
 | 
			
		||||
            volumeMounts:
 | 
			
		||||
              _HULL_OBJECT_TYPE_DEFAULT_: {}
 | 
			
		||||
        volumes: 
 | 
			
		||||
          _HULL_OBJECT_TYPE_DEFAULT_: {}
 | 
			
		||||
###################################################
 | 
			
		||||
 | 
			
		||||
# STATEFULSETS 
 | 
			
		||||
  statefulset:
 | 
			
		||||
    _HULL_OBJECT_TYPE_DEFAULT_:
 | 
			
		||||
      enabled: true
 | 
			
		||||
      annotations: {}
 | 
			
		||||
      labels: {}
 | 
			
		||||
      templateAnnotations: {}
 | 
			
		||||
      templateLabels: {}
 | 
			
		||||
      pod:
 | 
			
		||||
        initContainers: 
 | 
			
		||||
          _HULL_OBJECT_TYPE_DEFAULT_:
 | 
			
		||||
            env: 
 | 
			
		||||
              _HULL_OBJECT_TYPE_DEFAULT_: {}
 | 
			
		||||
            envFrom:
 | 
			
		||||
              _HULL_OBJECT_TYPE_DEFAULT_: {}
 | 
			
		||||
            volumeMounts:
 | 
			
		||||
              _HULL_OBJECT_TYPE_DEFAULT_: {}
 | 
			
		||||
        containers: 
 | 
			
		||||
          _HULL_OBJECT_TYPE_DEFAULT_:
 | 
			
		||||
            env: 
 | 
			
		||||
              _HULL_OBJECT_TYPE_DEFAULT_: {}
 | 
			
		||||
            envFrom:
 | 
			
		||||
              _HULL_OBJECT_TYPE_DEFAULT_: {}
 | 
			
		||||
            volumeMounts:
 | 
			
		||||
              _HULL_OBJECT_TYPE_DEFAULT_: {}
 | 
			
		||||
        volumes: 
 | 
			
		||||
          _HULL_OBJECT_TYPE_DEFAULT_: {}
 | 
			
		||||
###################################################
 | 
			
		||||
 | 
			
		||||
# SERVICEMONITORS 
 | 
			
		||||
  servicemonitor:
 | 
			
		||||
    _HULL_OBJECT_TYPE_DEFAULT_:
 | 
			
		||||
      enabled: true
 | 
			
		||||
      annotations: {}
 | 
			
		||||
      labels: {}
 | 
			
		||||
###################################################
 | 
			
		||||
 | 
			
		||||
# HORIZONTALPODAUTOSCALER
 | 
			
		||||
  horizontalpodautoscaler:
 | 
			
		||||
    _HULL_OBJECT_TYPE_DEFAULT_:
 | 
			
		||||
      enabled: true
 | 
			
		||||
      annotations: {}
 | 
			
		||||
      labels: {}
 | 
			
		||||
###################################################
 | 
			
		||||
 | 
			
		||||
# PODDISRUPTIONBUDGET
 | 
			
		||||
  poddisruptionbudget:
 | 
			
		||||
    _HULL_OBJECT_TYPE_DEFAULT_:
 | 
			
		||||
      enabled: true
 | 
			
		||||
      annotations: {}
 | 
			
		||||
      labels: {}
 | 
			
		||||
###################################################
 | 
			
		||||
 | 
			
		||||
# PRIORITYCLASS
 | 
			
		||||
  priorityclass:
 | 
			
		||||
    _HULL_OBJECT_TYPE_DEFAULT_:
 | 
			
		||||
      enabled: true
 | 
			
		||||
      annotations: {}
 | 
			
		||||
      labels: {}
 | 
			
		||||
###################################################
 | 
			
		||||
 | 
			
		||||
# ENDPOINTS
 | 
			
		||||
  endpoints:
 | 
			
		||||
    _HULL_OBJECT_TYPE_DEFAULT_:
 | 
			
		||||
      enabled: true
 | 
			
		||||
      annotations: {}
 | 
			
		||||
      labels: {}
 | 
			
		||||
###################################################
 | 
			
		||||
 | 
			
		||||
# ENDPOINTSLICE
 | 
			
		||||
  endpointslice:
 | 
			
		||||
    _HULL_OBJECT_TYPE_DEFAULT_:
 | 
			
		||||
      enabled: true
 | 
			
		||||
      annotations: {}
 | 
			
		||||
      labels: {}
 | 
			
		||||
###################################################
 | 
			
		||||
 | 
			
		||||
# LIMITRANGE
 | 
			
		||||
  limitrange:
 | 
			
		||||
    _HULL_OBJECT_TYPE_DEFAULT_:
 | 
			
		||||
      enabled: true
 | 
			
		||||
      annotations: {}
 | 
			
		||||
      labels: {}
 | 
			
		||||
###################################################
 | 
			
		||||
 | 
			
		||||
# MUTATINGWEBHOOKCONFIGURATION
 | 
			
		||||
  mutatingwebhookconfiguration:
 | 
			
		||||
    _HULL_OBJECT_TYPE_DEFAULT_:
 | 
			
		||||
      enabled: true
 | 
			
		||||
      annotations: {}
 | 
			
		||||
      labels: {}
 | 
			
		||||
      webhooks:
 | 
			
		||||
        _HULL_OBJECT_TYPE_DEFAULT_: {}
 | 
			
		||||
###################################################
 | 
			
		||||
 | 
			
		||||
# VALIDATINGWEBHOOKCONFIGURATION
 | 
			
		||||
  validatingwebhookconfiguration:
 | 
			
		||||
    _HULL_OBJECT_TYPE_DEFAULT_:
 | 
			
		||||
      enabled: true
 | 
			
		||||
      annotations: {}
 | 
			
		||||
      labels: {}
 | 
			
		||||
      webhooks:
 | 
			
		||||
        _HULL_OBJECT_TYPE_DEFAULT_: {}
 | 
			
		||||
###################################################
 | 
			
		||||
 | 
			
		||||
# RESOURCEQUOTA
 | 
			
		||||
  resourcequota:
 | 
			
		||||
    _HULL_OBJECT_TYPE_DEFAULT_:
 | 
			
		||||
      enabled: true
 | 
			
		||||
      annotations: {}
 | 
			
		||||
      labels: {}
 | 
			
		||||
##################################################
 | 
			
		||||
 | 
			
		||||
# NETWORKPOLICY
 | 
			
		||||
  networkpolicy:
 | 
			
		||||
    _HULL_OBJECT_TYPE_DEFAULT_:
 | 
			
		||||
      enabled: true
 | 
			
		||||
      annotations: {}
 | 
			
		||||
      labels: {}
 | 
			
		||||
##################################################
 | 
			
		||||
 | 
			
		||||
# GATEWAY API - BACKENDLBPOLICY
 | 
			
		||||
  backendlbpolicy:
 | 
			
		||||
    _HULL_OBJECT_TYPE_DEFAULT_:
 | 
			
		||||
      enabled: true
 | 
			
		||||
      annotations: {}
 | 
			
		||||
      labels: {}
 | 
			
		||||
      targetRefs:
 | 
			
		||||
        _HULL_OBJECT_TYPE_DEFAULT_: {}
 | 
			
		||||
      
 | 
			
		||||
##################################################
 | 
			
		||||
 | 
			
		||||
# GATEWAY API - BACKENDTLSPOLICY
 | 
			
		||||
  backendtlspolicy:
 | 
			
		||||
    _HULL_OBJECT_TYPE_DEFAULT_:
 | 
			
		||||
      enabled: true
 | 
			
		||||
      annotations: {}
 | 
			
		||||
      labels: {}
 | 
			
		||||
      targetRefs:
 | 
			
		||||
        _HULL_OBJECT_TYPE_DEFAULT_: {}
 | 
			
		||||
##################################################
 | 
			
		||||
 | 
			
		||||
# GATEWAY API - GATEWAYCLASS
 | 
			
		||||
  gatewayclass:
 | 
			
		||||
    _HULL_OBJECT_TYPE_DEFAULT_:
 | 
			
		||||
      enabled: true
 | 
			
		||||
      annotations: {}
 | 
			
		||||
      labels: {}
 | 
			
		||||
##################################################
 | 
			
		||||
 | 
			
		||||
# GATEWAY API - GATEWAY
 | 
			
		||||
  gateway:
 | 
			
		||||
    _HULL_OBJECT_TYPE_DEFAULT_:
 | 
			
		||||
      enabled: true
 | 
			
		||||
      annotations: {}
 | 
			
		||||
      labels: {}
 | 
			
		||||
      addresses: 
 | 
			
		||||
        _HULL_OBJECT_TYPE_DEFAULT_: {}
 | 
			
		||||
      listeners:
 | 
			
		||||
        _HULL_OBJECT_TYPE_DEFAULT_: 
 | 
			
		||||
          tls:
 | 
			
		||||
            certificateRefs:
 | 
			
		||||
              _HULL_OBJECT_TYPE_DEFAULT_: {}
 | 
			
		||||
            frontendValidation:
 | 
			
		||||
              caCertificateRefs:
 | 
			
		||||
                _HULL_OBJECT_TYPE_DEFAULT_: {}
 | 
			
		||||
          allowedRoutes:
 | 
			
		||||
            kinds:
 | 
			
		||||
              _HULL_OBJECT_TYPE_DEFAULT_: {}
 | 
			
		||||
##################################################
 | 
			
		||||
 | 
			
		||||
# GATEWAY API - GRPCROUTE
 | 
			
		||||
  grpcroute:
 | 
			
		||||
    _HULL_OBJECT_TYPE_DEFAULT_:
 | 
			
		||||
      enabled: true
 | 
			
		||||
      annotations: {}
 | 
			
		||||
      labels: {}
 | 
			
		||||
      parentRefs: 
 | 
			
		||||
        _HULL_OBJECT_TYPE_DEFAULT_: {}
 | 
			
		||||
      rules:
 | 
			
		||||
        _HULL_OBJECT_TYPE_DEFAULT_: 
 | 
			
		||||
          matches:
 | 
			
		||||
            _HULL_OBJECT_TYPE_DEFAULT_: {}
 | 
			
		||||
          filters:
 | 
			
		||||
            _HULL_OBJECT_TYPE_DEFAULT_: {}
 | 
			
		||||
          backendRefs:
 | 
			
		||||
            _HULL_OBJECT_TYPE_DEFAULT_:
 | 
			
		||||
              filters:
 | 
			
		||||
                _HULL_OBJECT_TYPE_DEFAULT_: {}
 | 
			
		||||
##################################################
 | 
			
		||||
 | 
			
		||||
# GATEWAY API - REFERENCEGRANT
 | 
			
		||||
  referencegrant:
 | 
			
		||||
    _HULL_OBJECT_TYPE_DEFAULT_:
 | 
			
		||||
      enabled: true
 | 
			
		||||
      annotations: {}
 | 
			
		||||
      labels: {}
 | 
			
		||||
      from: 
 | 
			
		||||
        _HULL_OBJECT_TYPE_DEFAULT_: {}
 | 
			
		||||
      to:
 | 
			
		||||
        _HULL_OBJECT_TYPE_DEFAULT_: {}
 | 
			
		||||
##################################################
 | 
			
		||||
 | 
			
		||||
# GATEWAY API - TCPROUTE
 | 
			
		||||
  tcproute:
 | 
			
		||||
    _HULL_OBJECT_TYPE_DEFAULT_:
 | 
			
		||||
      enabled: true
 | 
			
		||||
      annotations: {}
 | 
			
		||||
      labels: {}
 | 
			
		||||
      parentRefs: 
 | 
			
		||||
        _HULL_OBJECT_TYPE_DEFAULT_: {}
 | 
			
		||||
      rules:
 | 
			
		||||
        _HULL_OBJECT_TYPE_DEFAULT_: 
 | 
			
		||||
          backendRefs:
 | 
			
		||||
            _HULL_OBJECT_TYPE_DEFAULT_: {}
 | 
			
		||||
##################################################
 | 
			
		||||
 | 
			
		||||
# GATEWAY API - TLSROUTE
 | 
			
		||||
  tlsroute:
 | 
			
		||||
    _HULL_OBJECT_TYPE_DEFAULT_:
 | 
			
		||||
      enabled: true
 | 
			
		||||
      annotations: {}
 | 
			
		||||
      labels: {}
 | 
			
		||||
      parentRefs: 
 | 
			
		||||
        _HULL_OBJECT_TYPE_DEFAULT_: {}
 | 
			
		||||
      rules:
 | 
			
		||||
        _HULL_OBJECT_TYPE_DEFAULT_: 
 | 
			
		||||
          backendRefs:
 | 
			
		||||
            _HULL_OBJECT_TYPE_DEFAULT_: {}
 | 
			
		||||
##################################################
 | 
			
		||||
 | 
			
		||||
# GATEWAY API - UDPROUTE
 | 
			
		||||
  udproute:
 | 
			
		||||
    _HULL_OBJECT_TYPE_DEFAULT_:
 | 
			
		||||
      enabled: true
 | 
			
		||||
      annotations: {}
 | 
			
		||||
      labels: {}
 | 
			
		||||
      parentRefs: 
 | 
			
		||||
        _HULL_OBJECT_TYPE_DEFAULT_: {}
 | 
			
		||||
      rules:
 | 
			
		||||
        _HULL_OBJECT_TYPE_DEFAULT_: 
 | 
			
		||||
          backendRefs:
 | 
			
		||||
            _HULL_OBJECT_TYPE_DEFAULT_: {}
 | 
			
		||||
##################################################
 | 
			
		||||
 | 
			
		||||
# GATEWAY API - HTTPROUTE
 | 
			
		||||
  httproute:
 | 
			
		||||
    _HULL_OBJECT_TYPE_DEFAULT_:
 | 
			
		||||
      enabled: true
 | 
			
		||||
      annotations: {}
 | 
			
		||||
      labels: {}
 | 
			
		||||
      parentRefs: 
 | 
			
		||||
        _HULL_OBJECT_TYPE_DEFAULT_: {}
 | 
			
		||||
      rules:
 | 
			
		||||
        _HULL_OBJECT_TYPE_DEFAULT_: 
 | 
			
		||||
          matches:
 | 
			
		||||
            _HULL_OBJECT_TYPE_DEFAULT_: {}
 | 
			
		||||
          filters:
 | 
			
		||||
            _HULL_OBJECT_TYPE_DEFAULT_: {}
 | 
			
		||||
          backendRefs:
 | 
			
		||||
            _HULL_OBJECT_TYPE_DEFAULT_:
 | 
			
		||||
              filters:
 | 
			
		||||
                _HULL_OBJECT_TYPE_DEFAULT_: {}
 | 
			
		||||
##################################################
 | 
			
		||||
							
								
								
									
										1
									
								
								helm/ambient-local-exporter/templates/exporter.yaml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								helm/ambient-local-exporter/templates/exporter.yaml
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1 @@
 | 
			
		||||
{{- include "hull.objects.prepare.all" (dict "HULL_ROOT_KEY" "hull" "ROOT_CONTEXT" $) }}
 | 
			
		||||
							
								
								
									
										217
									
								
								helm/ambient-local-exporter/values.yaml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										217
									
								
								helm/ambient-local-exporter/values.yaml
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,217 @@
 | 
			
		||||
# yaml-language-server: $schema=https://raw.githubusercontent.com/vidispine/hull/refs/heads/main/hull/values.schema.json
 | 
			
		||||
hull:
 | 
			
		||||
  config:
 | 
			
		||||
    ## Ambient-Local-Exporter settings (config.yaml)
 | 
			
		||||
    appConfig:
 | 
			
		||||
      ## App Config
 | 
			
		||||
      environment: production
 | 
			
		||||
      name: ambient-local-exporter
 | 
			
		||||
      logging:
 | 
			
		||||
        level: info
 | 
			
		||||
        format: json
 | 
			
		||||
        output: stdout
 | 
			
		||||
        timeFormat: rfc3339
 | 
			
		||||
      http:
 | 
			
		||||
        listen: :8080
 | 
			
		||||
        logRequests: false
 | 
			
		||||
      grpc:
 | 
			
		||||
        enabled: true
 | 
			
		||||
        listen: :8081
 | 
			
		||||
        logRequests: true
 | 
			
		||||
        enableReflection: true
 | 
			
		||||
        enableInstrumentation: true
 | 
			
		||||
      otel:
 | 
			
		||||
        enabled: true
 | 
			
		||||
        metricIntervalSecs: 30
 | 
			
		||||
        stdoutEnabled: false
 | 
			
		||||
      ## Ambient Config
 | 
			
		||||
      metricPrefix: weather
 | 
			
		||||
      weatherStations:
 | 
			
		||||
        []
 | 
			
		||||
        # - name: Home Weather Station
 | 
			
		||||
        #   equipment: Ambient WS-2909
 | 
			
		||||
        #   awnPassKey: D3:AD:B3:3F:00:00
 | 
			
		||||
        #   proxyToAWN: true
 | 
			
		||||
        #   proxyToWunderground: false
 | 
			
		||||
        #   keepMetrics:
 | 
			
		||||
        #     - BaromAbsoluteIn
 | 
			
		||||
        #     - BaromRelativeIn
 | 
			
		||||
        #     - HumidityIndoor
 | 
			
		||||
        #     - StationType
 | 
			
		||||
        #     - TempIndoorF
 | 
			
		||||
        #   discardMetrics: []
 | 
			
		||||
        # - name: Shop Weather Station
 | 
			
		||||
        #   equipment: Ambient WS-5000
 | 
			
		||||
        #   awnPassKey: D3:AD:B3:3F:00:00
 | 
			
		||||
        #   wundergroundID: KINCOLUMXXX
 | 
			
		||||
        #   wundergroundPassword: somekey
 | 
			
		||||
        #   proxyToAWN: true
 | 
			
		||||
        #   proxyToWunderground: true
 | 
			
		||||
        #   keepMetrics: []
 | 
			
		||||
        #   discardMetrics: []
 | 
			
		||||
        #   sensorMappings:
 | 
			
		||||
        #     TempHumiditySensor1: Deep Freezer
 | 
			
		||||
    ## Chart settings
 | 
			
		||||
    settings:
 | 
			
		||||
      resources: {} # Applies to the exporter container
 | 
			
		||||
      repo: gitea.libretechconsulting.com/rmcguire/ambient-local-exporter
 | 
			
		||||
      tag: _HT**Chart.AppVersion
 | 
			
		||||
 | 
			
		||||
      httpPort: 8080 # Should match appConfig http.listen
 | 
			
		||||
      grpcPort: 8081 # Should match appConfig grpc.listen
 | 
			
		||||
 | 
			
		||||
      # Use this as a shortcut, or create your own hull.objects.httproute
 | 
			
		||||
      httproute:
 | 
			
		||||
        enabled: true
 | 
			
		||||
        hostnames:
 | 
			
		||||
          - ambient-local-exporter.mydomain.com
 | 
			
		||||
        gatewayName: istio-ingressgateway
 | 
			
		||||
        gatewayNamespace: istio-system
 | 
			
		||||
 | 
			
		||||
      # Use this as a shortcut, or create your own hull.objects.grpcroute
 | 
			
		||||
      grpcroute:
 | 
			
		||||
        enabled: true
 | 
			
		||||
        hostnames:
 | 
			
		||||
          - ambient-local-exporter.mydomain.com
 | 
			
		||||
        gatewayName: istio-ingressgateway
 | 
			
		||||
        gatewayNamespace: istio-system
 | 
			
		||||
 | 
			
		||||
      otelServiceName: ambient-local-exporter
 | 
			
		||||
      otelResourceAttributes: app=ambient-local-exporter
 | 
			
		||||
      otlpEndpoint: http://otel.otel.svc.cluster.local:4317 # Replace me
 | 
			
		||||
 | 
			
		||||
      serviceType: ClusterIP
 | 
			
		||||
      serviceLbIP: "" # Used if serviceTyps=LoadBalancer
 | 
			
		||||
 | 
			
		||||
    general:
 | 
			
		||||
      rbac: false
 | 
			
		||||
      render:
 | 
			
		||||
        passes: 2
 | 
			
		||||
      # Applies to all objects
 | 
			
		||||
      metadata:
 | 
			
		||||
        labels:
 | 
			
		||||
          custom:
 | 
			
		||||
            app: _HT**Release.Name
 | 
			
		||||
            version: _HT**Chart.AppVersion
 | 
			
		||||
 | 
			
		||||
  objects:
 | 
			
		||||
    configmap:
 | 
			
		||||
      config:
 | 
			
		||||
        data:
 | 
			
		||||
          config.yaml:
 | 
			
		||||
            serialization: toYaml
 | 
			
		||||
            inline: _HT*hull.config.appConfig
 | 
			
		||||
      environment:
 | 
			
		||||
        data:
 | 
			
		||||
          OTEL_EXPORTER_OTLP_ENDPOINT:
 | 
			
		||||
            serialization: none
 | 
			
		||||
            inline: _HT*hull.config.settings.otlpEndpoint
 | 
			
		||||
          OTEL_SERVICE_NAME:
 | 
			
		||||
            serialization: none
 | 
			
		||||
            inline: _HT*hull.config.settings.otelServiceName
 | 
			
		||||
          OTEL_RESOURCE_ATTRIBUTES:
 | 
			
		||||
            serialization: none
 | 
			
		||||
            inline: _HT!
 | 
			
		||||
              {{ printf "deployment.name=%s,%s" _HT**Release.Name _HT*hull.config.settings.otelResourceAttributes }}
 | 
			
		||||
    serviceaccount:
 | 
			
		||||
      default:
 | 
			
		||||
        enabled: false
 | 
			
		||||
    role:
 | 
			
		||||
      default:
 | 
			
		||||
        enabled: false
 | 
			
		||||
    rolebinding:
 | 
			
		||||
      default:
 | 
			
		||||
        enabled: false
 | 
			
		||||
 | 
			
		||||
    deployment:
 | 
			
		||||
      main:
 | 
			
		||||
        pod:
 | 
			
		||||
          containers:
 | 
			
		||||
            main:
 | 
			
		||||
              image:
 | 
			
		||||
                repository: _HT*hull.config.settings.repo
 | 
			
		||||
                tag: _HT!{{ printf "v%s" _HT*hull.config.settings.tag }}
 | 
			
		||||
              imagePullPolicy: Always
 | 
			
		||||
              args:
 | 
			
		||||
                - -config
 | 
			
		||||
                - /app/config.yaml
 | 
			
		||||
              ports:
 | 
			
		||||
                http:
 | 
			
		||||
                  containerPort: _HT*hull.config.settings.httpPort
 | 
			
		||||
                grpc:
 | 
			
		||||
                  containerPort: _HT*hull.config.settings.grpcPort
 | 
			
		||||
              envFrom:
 | 
			
		||||
                main:
 | 
			
		||||
                  configMapRef:
 | 
			
		||||
                    name: environment
 | 
			
		||||
              resources: _HT*hull.config.settings.resources
 | 
			
		||||
              readinessProbe:
 | 
			
		||||
                httpGet:
 | 
			
		||||
                  path: /health
 | 
			
		||||
                  port: 8080
 | 
			
		||||
                  scheme: HTTP
 | 
			
		||||
                periodSeconds: 10
 | 
			
		||||
                failureThreshold: 2
 | 
			
		||||
              livenessProbe:
 | 
			
		||||
                httpGet:
 | 
			
		||||
                  path: /health
 | 
			
		||||
                  port: 8080
 | 
			
		||||
                  scheme: HTTP
 | 
			
		||||
                periodSeconds: 10
 | 
			
		||||
                failureThreshold: 2
 | 
			
		||||
              volumeMounts:
 | 
			
		||||
                config:
 | 
			
		||||
                  name: config
 | 
			
		||||
                  mountPath: /app/config.yaml
 | 
			
		||||
                  subPath: config.yaml
 | 
			
		||||
          volumes:
 | 
			
		||||
            environment:
 | 
			
		||||
              configMap:
 | 
			
		||||
                name: environment
 | 
			
		||||
            config:
 | 
			
		||||
              configMap:
 | 
			
		||||
                name: config
 | 
			
		||||
 | 
			
		||||
    service:
 | 
			
		||||
      main:
 | 
			
		||||
        type: _HT*hull.config.settings.serviceType
 | 
			
		||||
        loadBalancerIP: _HT*hull.config.settings.serviceLbIP
 | 
			
		||||
        ports:
 | 
			
		||||
          http:
 | 
			
		||||
            port: _HT*hull.config.settings.httpPort
 | 
			
		||||
            targetPort: http
 | 
			
		||||
          grpc:
 | 
			
		||||
            port: _HT*hull.config.settings.grpcPort
 | 
			
		||||
            targetPort: grpc
 | 
			
		||||
 | 
			
		||||
    httproute:
 | 
			
		||||
      main:
 | 
			
		||||
        enabled: _HT*hull.config.settings.httproute.enabled
 | 
			
		||||
        hostnames: _HT*hull.config.settings.httproute.hostnames
 | 
			
		||||
        parentRefs:
 | 
			
		||||
          - group: gateway.networking.k8s.io
 | 
			
		||||
            kind: Gateway
 | 
			
		||||
            name: _HT*hull.config.settings.httproute.gatewayName
 | 
			
		||||
            namespace: _HT*hull.config.settings.httproute.gatewayNamespace
 | 
			
		||||
        rules:
 | 
			
		||||
          - backendRefs:
 | 
			
		||||
              - group: ""
 | 
			
		||||
                kind: Service
 | 
			
		||||
                name: _HT^main
 | 
			
		||||
                port: _HT*hull.config.settings.httpPort
 | 
			
		||||
 | 
			
		||||
    grpcroute:
 | 
			
		||||
      main:
 | 
			
		||||
        enabled: _HT*hull.config.settings.grpcroute.enabled
 | 
			
		||||
        hostnames: _HT*hull.config.settings.grpcroute.hostnames
 | 
			
		||||
        parentRefs:
 | 
			
		||||
          - group: gateway.networking.k8s.io
 | 
			
		||||
            kind: Gateway
 | 
			
		||||
            name: _HT*hull.config.settings.grpcroute.gatewayName
 | 
			
		||||
            namespace: _HT*hull.config.settings.grpcroute.gatewayNamespace
 | 
			
		||||
        rules:
 | 
			
		||||
          - backendRefs:
 | 
			
		||||
              - group: ""
 | 
			
		||||
                kind: Service
 | 
			
		||||
                name: _HT^main
 | 
			
		||||
                port: _HT*hull.config.settings.grpcPort
 | 
			
		||||
							
								
								
									
										31
									
								
								main.go
									
									
									
									
									
								
							
							
						
						
									
										31
									
								
								main.go
									
									
									
									
									
								
							@@ -6,17 +6,21 @@ import (
 | 
			
		||||
	"os/signal"
 | 
			
		||||
 | 
			
		||||
	"gitea.libretechconsulting.com/rmcguire/go-app/pkg/app"
 | 
			
		||||
	"gitea.libretechconsulting.com/rmcguire/go-app/pkg/srv"
 | 
			
		||||
	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"
 | 
			
		||||
 | 
			
		||||
	"gitea.libretechconsulting.com/rmcguire/ambient-weather-local-exporter/pkg/ambient"
 | 
			
		||||
	"gitea.libretechconsulting.com/rmcguire/ambient-weather-local-exporter/pkg/ambient/config"
 | 
			
		||||
	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"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
const defaultMetricPrefix = "weather"
 | 
			
		||||
 | 
			
		||||
func main() {
 | 
			
		||||
	ctx, cncl := signal.NotifyContext(context.Background(), os.Kill, unix.SIGTERM)
 | 
			
		||||
	ctx, cncl := signal.NotifyContext(context.Background(), os.Kill, os.Interrupt, unix.SIGTERM)
 | 
			
		||||
	defer cncl()
 | 
			
		||||
 | 
			
		||||
	// Config type for app, which implements go-app/config.AppConfig
 | 
			
		||||
@@ -35,8 +39,9 @@ func main() {
 | 
			
		||||
	// Define and prepare the app
 | 
			
		||||
	awApp := app.App{
 | 
			
		||||
		AppContext: ctx,
 | 
			
		||||
		HTTP: &app.AppHTTP{
 | 
			
		||||
			Funcs: []srv.HTTPFunc{
 | 
			
		||||
		// HTTP Endpoints for Ambient Weather Stations
 | 
			
		||||
		HTTP: &httpopts.AppHTTP{
 | 
			
		||||
			Funcs: []httpopts.HTTPFunc{
 | 
			
		||||
				{
 | 
			
		||||
					Path:        "/weatherstation/updateweatherstation.php",
 | 
			
		||||
					HandlerFunc: aw.GetWundergroundHandlerFunc(ctx),
 | 
			
		||||
@@ -46,13 +51,25 @@ func main() {
 | 
			
		||||
					HandlerFunc: aw.GetAWNHandlerFunc(ctx),
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			HealthChecks: []srv.HealthCheckFunc{
 | 
			
		||||
			CustomListener: ambienthttp.NewAWNMutatingListener(ctx,
 | 
			
		||||
				awConfig.HTTP.Listen), // Necessary to fix certain bad AWN firmware
 | 
			
		||||
			HealthChecks: []httpopts.HealthCheckFunc{
 | 
			
		||||
				// TODO: Implement
 | 
			
		||||
				func(ctx context.Context) error {
 | 
			
		||||
					return nil
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
		// GRPC Service for retrieving current weather
 | 
			
		||||
		GRPC: &grpcopts.AppGRPC{
 | 
			
		||||
			Services: []*grpcopts.GRPCService{
 | 
			
		||||
				{
 | 
			
		||||
					Name:    "Weather Service",
 | 
			
		||||
					Type:    &weatherpb.AmbientLocalWeatherService_ServiceDesc,
 | 
			
		||||
					Service: weathergrpc.GRPCWeather{},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Run and wait
 | 
			
		||||
 
 | 
			
		||||
@@ -13,12 +13,13 @@ import (
 | 
			
		||||
	"github.com/rs/zerolog"
 | 
			
		||||
	"go.opentelemetry.io/otel/attribute"
 | 
			
		||||
	"go.opentelemetry.io/otel/codes"
 | 
			
		||||
	"go.opentelemetry.io/otel/trace"
 | 
			
		||||
 | 
			
		||||
	"gitea.libretechconsulting.com/rmcguire/ambient-weather-local-exporter/pkg/ambient/config"
 | 
			
		||||
	"gitea.libretechconsulting.com/rmcguire/ambient-weather-local-exporter/pkg/provider"
 | 
			
		||||
	"gitea.libretechconsulting.com/rmcguire/ambient-weather-local-exporter/pkg/provider/awn"
 | 
			
		||||
	"gitea.libretechconsulting.com/rmcguire/ambient-weather-local-exporter/pkg/provider/wunderground"
 | 
			
		||||
	"gitea.libretechconsulting.com/rmcguire/ambient-weather-local-exporter/pkg/weather"
 | 
			
		||||
	"gitea.libretechconsulting.com/rmcguire/ambient-local-exporter/pkg/ambient/config"
 | 
			
		||||
	"gitea.libretechconsulting.com/rmcguire/ambient-local-exporter/pkg/provider"
 | 
			
		||||
	"gitea.libretechconsulting.com/rmcguire/ambient-local-exporter/pkg/provider/awn"
 | 
			
		||||
	"gitea.libretechconsulting.com/rmcguire/ambient-local-exporter/pkg/provider/wunderground"
 | 
			
		||||
	"gitea.libretechconsulting.com/rmcguire/ambient-local-exporter/pkg/weather"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type AmbientWeather struct {
 | 
			
		||||
@@ -67,6 +68,11 @@ func (aw *AmbientWeather) GetWundergroundHandlerFunc(appCtx context.Context) fun
 | 
			
		||||
// stable type. Enrich is called on the type to complete
 | 
			
		||||
// any missing fields as the two providers supported by Ambient
 | 
			
		||||
// devices (awn/wunderground) produce different fields
 | 
			
		||||
//
 | 
			
		||||
// This will call Update on metrics, and will also proxy
 | 
			
		||||
// requests to AWN/Wunderground if enabled
 | 
			
		||||
// This is the main work performed when a weather station or
 | 
			
		||||
// weather hub sends an update
 | 
			
		||||
func (aw *AmbientWeather) handleProviderRequest(
 | 
			
		||||
	p provider.AmbientProvider,
 | 
			
		||||
	w http.ResponseWriter,
 | 
			
		||||
@@ -75,9 +81,9 @@ func (aw *AmbientWeather) handleProviderRequest(
 | 
			
		||||
	l := zerolog.Ctx(aw.appCtx)
 | 
			
		||||
	tracer := otel.GetTracer(aw.appCtx, p.Name()+".http.handler")
 | 
			
		||||
 | 
			
		||||
	ctx, span := tracer.Start(r.Context(), p.Name()+".update")
 | 
			
		||||
	span.SetAttributes(attribute.String("provider", p.Name()))
 | 
			
		||||
	defer span.End()
 | 
			
		||||
	ctx, updateSpan := tracer.Start(r.Context(), p.Name()+".update")
 | 
			
		||||
	updateSpan.SetAttributes(attribute.String("provider", p.Name()))
 | 
			
		||||
	defer updateSpan.End()
 | 
			
		||||
 | 
			
		||||
	l.Trace().Str("p", p.Name()).
 | 
			
		||||
		Any("query", r.URL.Query()).Send()
 | 
			
		||||
@@ -86,8 +92,8 @@ func (aw *AmbientWeather) handleProviderRequest(
 | 
			
		||||
	update, err := p.ReqToWeather(ctx, r)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		l.Err(err).Send()
 | 
			
		||||
		span.RecordError(err)
 | 
			
		||||
		span.SetStatus(codes.Error,
 | 
			
		||||
		updateSpan.RecordError(err)
 | 
			
		||||
		updateSpan.SetStatus(codes.Error,
 | 
			
		||||
			fmt.Sprintf("failed to handle %s update: %s",
 | 
			
		||||
				p.Name(), err.Error()))
 | 
			
		||||
 | 
			
		||||
@@ -96,20 +102,16 @@ func (aw *AmbientWeather) handleProviderRequest(
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Calculate any fields that may be missing
 | 
			
		||||
	// such as dew point and wind chill
 | 
			
		||||
	update.Enrich()
 | 
			
		||||
	// Perform enrichment
 | 
			
		||||
	aw.enrichUpdate(ctx, p, update)
 | 
			
		||||
 | 
			
		||||
	// Prepare metrics if this is the first update
 | 
			
		||||
	if aw.metrics == nil {
 | 
			
		||||
		aw.InitMetrics()
 | 
			
		||||
	// We may know which station this was for now
 | 
			
		||||
	if update.StationConfig != nil {
 | 
			
		||||
		updateSpan.SetAttributes(attribute.String("stationName", update.StationConfig.Name))
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Enrich station if configured
 | 
			
		||||
	aw.enrichStation(update)
 | 
			
		||||
 | 
			
		||||
	// Update metrics
 | 
			
		||||
	aw.metrics.Update(update)
 | 
			
		||||
	aw.metricsUpdate(ctx, p, update)
 | 
			
		||||
 | 
			
		||||
	l.Debug().
 | 
			
		||||
		Str("provider", p.Name()).
 | 
			
		||||
@@ -120,44 +122,110 @@ func (aw *AmbientWeather) handleProviderRequest(
 | 
			
		||||
	// Proxy update to one or both services if configured to do so
 | 
			
		||||
	// Uses a weather update to allow awn to publish to wunderground and
 | 
			
		||||
	// visa versa.
 | 
			
		||||
	if station := update.StationConfig; station != nil {
 | 
			
		||||
		// Perform proxy updates in parallel if enabled
 | 
			
		||||
		var proxyWg sync.WaitGroup
 | 
			
		||||
 | 
			
		||||
		if station.ProxyToAWN {
 | 
			
		||||
			proxyWg.Add(1)
 | 
			
		||||
			go func() {
 | 
			
		||||
				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 {
 | 
			
		||||
			proxyWg.Add(1)
 | 
			
		||||
			go func() {
 | 
			
		||||
				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()
 | 
			
		||||
	if update.StationConfig != nil {
 | 
			
		||||
		aw.proxyUpdate(ctx, p, update)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (aw *AmbientWeather) enrichUpdate(
 | 
			
		||||
	ctx context.Context,
 | 
			
		||||
	p provider.AmbientProvider,
 | 
			
		||||
	update *weather.WeatherUpdate,
 | 
			
		||||
) {
 | 
			
		||||
	tracer := otel.GetTracer(aw.appCtx, p.Name()+".http.handler")
 | 
			
		||||
 | 
			
		||||
	// Calculate any fields that may be missing
 | 
			
		||||
	// such as dew point and wind chill
 | 
			
		||||
	_, enrichSpan := tracer.Start(ctx, p.Name()+".update.enrich")
 | 
			
		||||
	defer enrichSpan.End()
 | 
			
		||||
 | 
			
		||||
	// Metric enrichment
 | 
			
		||||
	update.Enrich()
 | 
			
		||||
 | 
			
		||||
	// Enrich station if configured
 | 
			
		||||
	aw.enrichStation(update)
 | 
			
		||||
 | 
			
		||||
	// Map sensor names
 | 
			
		||||
	update.MapSensors()
 | 
			
		||||
 | 
			
		||||
	enrichSpan.SetStatus(codes.Ok, "")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (aw *AmbientWeather) metricsUpdate(
 | 
			
		||||
	ctx context.Context,
 | 
			
		||||
	p provider.AmbientProvider,
 | 
			
		||||
	update *weather.WeatherUpdate,
 | 
			
		||||
) {
 | 
			
		||||
	tracer := otel.GetTracer(aw.appCtx, p.Name()+".http.handler")
 | 
			
		||||
	_, metricsSpan := tracer.Start(ctx, p.Name()+".update.metrics")
 | 
			
		||||
 | 
			
		||||
	if aw.metrics == nil {
 | 
			
		||||
		aw.InitMetrics()
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	aw.metrics.Update(update)
 | 
			
		||||
 | 
			
		||||
	metricsSpan.SetStatus(codes.Ok, "")
 | 
			
		||||
	metricsSpan.End()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (aw *AmbientWeather) proxyUpdate(
 | 
			
		||||
	ctx context.Context,
 | 
			
		||||
	p provider.AmbientProvider,
 | 
			
		||||
	update *weather.WeatherUpdate,
 | 
			
		||||
) {
 | 
			
		||||
	var proxyWg sync.WaitGroup
 | 
			
		||||
 | 
			
		||||
	tracer := otel.GetTracer(aw.appCtx, p.Name()+".http.handler")
 | 
			
		||||
	station := update.StationConfig
 | 
			
		||||
 | 
			
		||||
	ctx, proxySpan := tracer.Start(ctx, p.Name()+".update.proxy", trace.WithAttributes(
 | 
			
		||||
		attribute.Bool("proxyToWunderground", station.ProxyToWunderground),
 | 
			
		||||
		attribute.Bool("proxyToAWN", station.ProxyToAWN),
 | 
			
		||||
	))
 | 
			
		||||
	defer proxySpan.End()
 | 
			
		||||
 | 
			
		||||
	// Perform proxy updates in parallel if enabled
 | 
			
		||||
 | 
			
		||||
	if station.ProxyToAWN {
 | 
			
		||||
		proxyWg.Add(1)
 | 
			
		||||
		go func() {
 | 
			
		||||
			defer proxyWg.Done()
 | 
			
		||||
			defer proxySpan.AddEvent("proxied to ambient weather network")
 | 
			
		||||
			err := aw.awnProvider.ProxyReq(ctx, update)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				zerolog.Ctx(aw.appCtx).Err(err).Msg("failed to proxy to ambient weather")
 | 
			
		||||
				proxySpan.RecordError(err)
 | 
			
		||||
				proxySpan.SetStatus(codes.Error, err.Error())
 | 
			
		||||
				return
 | 
			
		||||
			}
 | 
			
		||||
			zerolog.Ctx(aw.appCtx).Debug().
 | 
			
		||||
				Str("station", station.Name).
 | 
			
		||||
				Msg("proxied weather update to awn")
 | 
			
		||||
		}()
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if station.ProxyToWunderground {
 | 
			
		||||
		proxyWg.Add(1)
 | 
			
		||||
		go func() {
 | 
			
		||||
			defer proxyWg.Done()
 | 
			
		||||
			defer proxySpan.AddEvent("proxied to wunderground")
 | 
			
		||||
			err := aw.wuProvider.ProxyReq(ctx, update)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				zerolog.Ctx(aw.appCtx).Err(err).Msg("failed to proxy to ambient weather")
 | 
			
		||||
				proxySpan.RecordError(err)
 | 
			
		||||
				proxySpan.SetStatus(codes.Error, err.Error())
 | 
			
		||||
				return
 | 
			
		||||
			}
 | 
			
		||||
			zerolog.Ctx(aw.appCtx).Debug().
 | 
			
		||||
				Str("station", station.Name).
 | 
			
		||||
				Msg("proxied weather update to wunderground")
 | 
			
		||||
		}()
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	proxyWg.Wait()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (aw *AmbientWeather) InitMetrics() {
 | 
			
		||||
	if aw.config.MetricPrefix != "" {
 | 
			
		||||
		weather.MetricPrefix = aw.config.MetricPrefix
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										112
									
								
								pkg/ambient/ambienthttp/ambienthttp.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										112
									
								
								pkg/ambient/ambienthttp/ambienthttp.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,112 @@
 | 
			
		||||
// This package exists purely to override the net.Listener used
 | 
			
		||||
// by the application's http server. This is necessary for certain versions
 | 
			
		||||
// of firmware which errantly put an 0x0a (LF) following PASSKEY for
 | 
			
		||||
// AmbientWeather type http reporting.
 | 
			
		||||
//
 | 
			
		||||
// This needs to be fixed upstream by Ambient Weather and is a complete
 | 
			
		||||
// hack that should never be necessary. Without this, the http server
 | 
			
		||||
// will silently crank back an HTTP:400
 | 
			
		||||
package ambienthttp
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"bufio"
 | 
			
		||||
	"bytes"
 | 
			
		||||
	"context"
 | 
			
		||||
	"io"
 | 
			
		||||
	"net"
 | 
			
		||||
	"regexp"
 | 
			
		||||
 | 
			
		||||
	"github.com/rs/zerolog"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Invalid Request Pattern
 | 
			
		||||
var badReqURI = regexp.MustCompile(`PASSKEY=[^\n&]{16,}$`)
 | 
			
		||||
 | 
			
		||||
// Listener encapsulates LFStrippingConn to perform
 | 
			
		||||
// infuriating strip of newline character present after PASSKEY
 | 
			
		||||
// sent errantly by specific versions of firmware sending updates
 | 
			
		||||
// in AmbientWeather protocol
 | 
			
		||||
type LFStrippingListener struct {
 | 
			
		||||
	ctx context.Context
 | 
			
		||||
	net.Listener
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type LFStrippingConn struct {
 | 
			
		||||
	ctx    context.Context
 | 
			
		||||
	reader io.Reader
 | 
			
		||||
	net.Conn
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (l *LFStrippingListener) WrapConn(conn net.Conn) net.Conn {
 | 
			
		||||
	buf := new(bytes.Buffer)
 | 
			
		||||
	reader := io.TeeReader(conn, buf)
 | 
			
		||||
 | 
			
		||||
	scanner := bufio.NewScanner(reader)
 | 
			
		||||
	var newData []byte
 | 
			
		||||
	for scanner.Scan() {
 | 
			
		||||
		line := scanner.Bytes()
 | 
			
		||||
		newData = append(newData, line...)
 | 
			
		||||
 | 
			
		||||
		// Only restore newline if not a bad request
 | 
			
		||||
		if !badReqURI.Match(line) {
 | 
			
		||||
			newData = append(newData, '\n')
 | 
			
		||||
		} else {
 | 
			
		||||
			zerolog.Ctx(l.ctx).Warn().Bytes("line", line).
 | 
			
		||||
				Msg("malformed request found, stripped 0x0a")
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if len(line) == 0 {
 | 
			
		||||
			break
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	if scanner.Err() != nil {
 | 
			
		||||
		zerolog.Ctx(l.ctx).Err(scanner.Err()).Send()
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	zerolog.Ctx(l.ctx).Trace().
 | 
			
		||||
		Int("numBytes", len(newData)).
 | 
			
		||||
		Bytes("request", newData).
 | 
			
		||||
		Msg("stripping conn complete")
 | 
			
		||||
 | 
			
		||||
	// Use a multi-reader to prepend the modified request
 | 
			
		||||
	finalReader := io.MultiReader(bytes.NewReader(newData), conn)
 | 
			
		||||
 | 
			
		||||
	return &LFStrippingConn{
 | 
			
		||||
		Conn:   conn,
 | 
			
		||||
		ctx:    l.ctx,
 | 
			
		||||
		reader: finalReader,
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func NewAWNMutatingListener(ctx context.Context, listen string) net.Listener {
 | 
			
		||||
	rawListener, err := net.Listen("tcp", listen)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		panic(err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Encapsulate the raw listener with ours
 | 
			
		||||
	return &LFStrippingListener{
 | 
			
		||||
		Listener: rawListener,
 | 
			
		||||
		ctx:      ctx,
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (l *LFStrippingListener) Accept() (net.Conn, error) {
 | 
			
		||||
	conn, err := l.Listener.Accept()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	return l.WrapConn(conn), nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (l *LFStrippingListener) Close() error {
 | 
			
		||||
	return l.Listener.Close()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (l *LFStrippingListener) Addr() net.Addr {
 | 
			
		||||
	return l.Listener.Addr()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (c *LFStrippingConn) Read(b []byte) (int, error) {
 | 
			
		||||
	return c.reader.Read(b)
 | 
			
		||||
}
 | 
			
		||||
@@ -33,4 +33,14 @@ type WeatherStation struct {
 | 
			
		||||
	// Check weather.WeatherUpdateField for options
 | 
			
		||||
	KeepMetrics []string `yaml:"keepMetrics"`
 | 
			
		||||
	DropMetrics []string `yaml:"dropMetrics"`
 | 
			
		||||
 | 
			
		||||
	// Relabels battery and sensor names
 | 
			
		||||
	// Temp+Humidity Sensors:
 | 
			
		||||
	//   - TempHumiditySensor[1-8]
 | 
			
		||||
	// Batteries:
 | 
			
		||||
	//   - IndoorSensor
 | 
			
		||||
	//   - OutdoorSensor
 | 
			
		||||
	//   - RainSensor
 | 
			
		||||
	//   - CO2Sensor
 | 
			
		||||
	SensorMappings map[string]string `yaml:"sensorMappings"`
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										12
									
								
								pkg/ambient/config/ws_map.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								pkg/ambient/config/ws_map.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,12 @@
 | 
			
		||||
package config
 | 
			
		||||
 | 
			
		||||
// If the weather-station has a mapping, returns the new
 | 
			
		||||
// name for the sensor
 | 
			
		||||
func (ws *WeatherStation) MapSensor(sensor string) string {
 | 
			
		||||
	for name, replacement := range ws.SensorMappings {
 | 
			
		||||
		if name == sensor && replacement != "" {
 | 
			
		||||
			return replacement
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return sensor
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										67
									
								
								pkg/ambient/config/ws_map_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										67
									
								
								pkg/ambient/config/ws_map_test.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,67 @@
 | 
			
		||||
package config
 | 
			
		||||
 | 
			
		||||
import "testing"
 | 
			
		||||
 | 
			
		||||
func TestWeatherStation_MapSensor(t *testing.T) {
 | 
			
		||||
	type fields struct {
 | 
			
		||||
		Name                 string
 | 
			
		||||
		Equipment            string
 | 
			
		||||
		WundergroundID       string
 | 
			
		||||
		WundergroundPassword string
 | 
			
		||||
		AWNPassKey           string
 | 
			
		||||
		ProxyToAWN           bool
 | 
			
		||||
		ProxyToWunderground  bool
 | 
			
		||||
		KeepMetrics          []string
 | 
			
		||||
		DropMetrics          []string
 | 
			
		||||
		SensorMappings       map[string]string
 | 
			
		||||
	}
 | 
			
		||||
	type args struct {
 | 
			
		||||
		sensor string
 | 
			
		||||
	}
 | 
			
		||||
	tests := []struct {
 | 
			
		||||
		name   string
 | 
			
		||||
		fields fields
 | 
			
		||||
		args   args
 | 
			
		||||
		want   string
 | 
			
		||||
	}{
 | 
			
		||||
		{
 | 
			
		||||
			name: "Check sensor mapping",
 | 
			
		||||
			fields: fields{
 | 
			
		||||
				SensorMappings: map[string]string{
 | 
			
		||||
					"TempHumiditySensor1": "TestSensor",
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			args: args{sensor: "TempHumiditySensor1"},
 | 
			
		||||
			want: "TestSensor",
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name: "Check sensor no mapping",
 | 
			
		||||
			fields: fields{
 | 
			
		||||
				SensorMappings: map[string]string{
 | 
			
		||||
					"TempHumiditySensor1": "TestSensor",
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			args: args{sensor: "TempHumiditySensor2"},
 | 
			
		||||
			want: "TempHumiditySensor2",
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
	for _, tt := range tests {
 | 
			
		||||
		t.Run(tt.name, func(t *testing.T) {
 | 
			
		||||
			ws := &WeatherStation{
 | 
			
		||||
				Name:                 tt.fields.Name,
 | 
			
		||||
				Equipment:            tt.fields.Equipment,
 | 
			
		||||
				WundergroundID:       tt.fields.WundergroundID,
 | 
			
		||||
				WundergroundPassword: tt.fields.WundergroundPassword,
 | 
			
		||||
				AWNPassKey:           tt.fields.AWNPassKey,
 | 
			
		||||
				ProxyToAWN:           tt.fields.ProxyToAWN,
 | 
			
		||||
				ProxyToWunderground:  tt.fields.ProxyToWunderground,
 | 
			
		||||
				KeepMetrics:          tt.fields.KeepMetrics,
 | 
			
		||||
				DropMetrics:          tt.fields.DropMetrics,
 | 
			
		||||
				SensorMappings:       tt.fields.SensorMappings,
 | 
			
		||||
			}
 | 
			
		||||
			if got := ws.MapSensor(tt.args.sensor); got != tt.want {
 | 
			
		||||
				t.Errorf("WeatherStation.MapSensor() = %v, want %v", got, tt.want)
 | 
			
		||||
			}
 | 
			
		||||
		})
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
@@ -8,7 +8,7 @@ import (
 | 
			
		||||
 | 
			
		||||
	"github.com/gorilla/schema"
 | 
			
		||||
 | 
			
		||||
	"gitea.libretechconsulting.com/rmcguire/ambient-weather-local-exporter/pkg/weather"
 | 
			
		||||
	"gitea.libretechconsulting.com/rmcguire/ambient-local-exporter/pkg/weather"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type AWNProvider struct{}
 | 
			
		||||
@@ -24,6 +24,7 @@ const (
 | 
			
		||||
	BattIndoorSensor  = "IndoorSensor"
 | 
			
		||||
	BattRainSensor    = "RainSensor"
 | 
			
		||||
	BattCO2Sensor     = "CO2Sensor"
 | 
			
		||||
	THSensor          = "TempHumiditySensor"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func (awn *AWNProvider) Name() string {
 | 
			
		||||
@@ -89,11 +90,54 @@ func MapAwnUpdate(awnUpdate *AmbientWeatherUpdate) *weather.WeatherUpdate {
 | 
			
		||||
				Component: BattCO2Sensor,
 | 
			
		||||
				Status:    awnUpdate.BattCO2,
 | 
			
		||||
			},
 | 
			
		||||
			// Temp and Humidity Sensors
 | 
			
		||||
			{
 | 
			
		||||
				Component: THSensor + "1",
 | 
			
		||||
				Status:    awnUpdate.Batt1,
 | 
			
		||||
			},
 | 
			
		||||
			{
 | 
			
		||||
				Component: THSensor + "2",
 | 
			
		||||
				Status:    awnUpdate.Batt2,
 | 
			
		||||
			},
 | 
			
		||||
			{
 | 
			
		||||
				Component: THSensor + "3",
 | 
			
		||||
				Status:    awnUpdate.Batt3,
 | 
			
		||||
			},
 | 
			
		||||
			{
 | 
			
		||||
				Component: THSensor + "4",
 | 
			
		||||
				Status:    awnUpdate.Batt4,
 | 
			
		||||
			},
 | 
			
		||||
			{
 | 
			
		||||
				Component: THSensor + "5",
 | 
			
		||||
				Status:    awnUpdate.Batt5,
 | 
			
		||||
			},
 | 
			
		||||
			{
 | 
			
		||||
				Component: THSensor + "6",
 | 
			
		||||
				Status:    awnUpdate.Batt6,
 | 
			
		||||
			},
 | 
			
		||||
			{
 | 
			
		||||
				Component: THSensor + "7",
 | 
			
		||||
				Status:    awnUpdate.Batt7,
 | 
			
		||||
			},
 | 
			
		||||
			{
 | 
			
		||||
				Component: THSensor + "8",
 | 
			
		||||
				Status:    awnUpdate.Batt8,
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
		TempIndoorF:     awnUpdate.TempInF,
 | 
			
		||||
		HumidityIndoor:  awnUpdate.HumidityIn,
 | 
			
		||||
		BaromRelativeIn: awnUpdate.BaromRelIn,
 | 
			
		||||
		BaromAbsoluteIn: awnUpdate.BaromAbsIn,
 | 
			
		||||
		TempHumiditySensors: []*weather.TempHumiditySensor{
 | 
			
		||||
			{Name: THSensor + "1", TempF: awnUpdate.Temp1F, Humidity: awnUpdate.Humidity1},
 | 
			
		||||
			{Name: THSensor + "2", TempF: awnUpdate.Temp2F, Humidity: awnUpdate.Humidity2},
 | 
			
		||||
			{Name: THSensor + "3", TempF: awnUpdate.Temp3F, Humidity: awnUpdate.Humidity3},
 | 
			
		||||
			{Name: THSensor + "4", TempF: awnUpdate.Temp4F, Humidity: awnUpdate.Humidity4},
 | 
			
		||||
			{Name: THSensor + "5", TempF: awnUpdate.Temp5F, Humidity: awnUpdate.Humidity5},
 | 
			
		||||
			{Name: THSensor + "6", TempF: awnUpdate.Temp6F, Humidity: awnUpdate.Humidity6},
 | 
			
		||||
			{Name: THSensor + "7", TempF: awnUpdate.Temp7F, Humidity: awnUpdate.Humidity7},
 | 
			
		||||
			{Name: THSensor + "8", TempF: awnUpdate.Temp8F, Humidity: awnUpdate.Humidity8},
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -12,7 +12,7 @@ import (
 | 
			
		||||
	"go.opentelemetry.io/otel/attribute"
 | 
			
		||||
	"go.opentelemetry.io/otel/codes"
 | 
			
		||||
 | 
			
		||||
	"gitea.libretechconsulting.com/rmcguire/ambient-weather-local-exporter/pkg/weather"
 | 
			
		||||
	"gitea.libretechconsulting.com/rmcguire/ambient-local-exporter/pkg/weather"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Attempts to proxy the weather station update to awn
 | 
			
		||||
 
 | 
			
		||||
@@ -28,4 +28,32 @@ type AmbientWeatherUpdate struct {
 | 
			
		||||
	BaromAbsIn     *float64 `json:"baromabsin,omitempty" schema:"baromabsin"`
 | 
			
		||||
	BattIn         *int     `json:"battin,omitempty" schema:"battin"`
 | 
			
		||||
	BattCO2        *int     `json:"batt_co2,omitempty" schema:"batt_co2"`
 | 
			
		||||
	*AmbientTempHumiditySensors
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type AmbientTempHumiditySensors struct {
 | 
			
		||||
	Temp1F    *float64 `json:"temp1f,omitempty" schema:"temp1f"`
 | 
			
		||||
	Temp2F    *float64 `json:"temp2f,omitempty" schema:"temp2f"`
 | 
			
		||||
	Temp3F    *float64 `json:"temp3f,omitempty" schema:"temp3f"`
 | 
			
		||||
	Temp4F    *float64 `json:"temp4f,omitempty" schema:"temp4f"`
 | 
			
		||||
	Temp5F    *float64 `json:"temp5f,omitempty" schema:"temp5f"`
 | 
			
		||||
	Temp6F    *float64 `json:"temp6f,omitempty" schema:"temp6f"`
 | 
			
		||||
	Temp7F    *float64 `json:"temp7f,omitempty" schema:"temp7f"`
 | 
			
		||||
	Temp8F    *float64 `json:"temp8f,omitempty" schema:"temp8f"`
 | 
			
		||||
	Humidity1 *int     `json:"humidity1,omitempty" schema:"humidity1"`
 | 
			
		||||
	Humidity2 *int     `json:"humidity2,omitempty" schema:"humidity2"`
 | 
			
		||||
	Humidity3 *int     `json:"humidity3,omitempty" schema:"humidity3"`
 | 
			
		||||
	Humidity4 *int     `json:"humidity4,omitempty" schema:"humidity4"`
 | 
			
		||||
	Humidity5 *int     `json:"humidity5,omitempty" schema:"humidity5"`
 | 
			
		||||
	Humidity6 *int     `json:"humidity6,omitempty" schema:"humidity6"`
 | 
			
		||||
	Humidity7 *int     `json:"humidity7,omitempty" schema:"humidity7"`
 | 
			
		||||
	Humidity8 *int     `json:"humidity8,omitempty" schema:"humidity8"`
 | 
			
		||||
	Batt1     *int     `json:"batt1,omitempty" schema:"batt1"`
 | 
			
		||||
	Batt2     *int     `json:"batt2,omitempty" schema:"batt2"`
 | 
			
		||||
	Batt3     *int     `json:"batt3,omitempty" schema:"batt3"`
 | 
			
		||||
	Batt4     *int     `json:"batt4,omitempty" schema:"batt4"`
 | 
			
		||||
	Batt5     *int     `json:"batt5,omitempty" schema:"batt5"`
 | 
			
		||||
	Batt6     *int     `json:"batt6,omitempty" schema:"batt6"`
 | 
			
		||||
	Batt7     *int     `json:"batt7,omitempty" schema:"batt7"`
 | 
			
		||||
	Batt8     *int     `json:"batt8,omitempty" schema:"batt8"`
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -4,7 +4,7 @@ import (
 | 
			
		||||
	"context"
 | 
			
		||||
	"net/http"
 | 
			
		||||
 | 
			
		||||
	"gitea.libretechconsulting.com/rmcguire/ambient-weather-local-exporter/pkg/weather"
 | 
			
		||||
	"gitea.libretechconsulting.com/rmcguire/ambient-local-exporter/pkg/weather"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Simple interface used for converting Wunderground and
 | 
			
		||||
 
 | 
			
		||||
@@ -8,7 +8,7 @@ import (
 | 
			
		||||
 | 
			
		||||
	"github.com/gorilla/schema"
 | 
			
		||||
 | 
			
		||||
	"gitea.libretechconsulting.com/rmcguire/ambient-weather-local-exporter/pkg/weather"
 | 
			
		||||
	"gitea.libretechconsulting.com/rmcguire/ambient-local-exporter/pkg/weather"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type WUProvider struct{}
 | 
			
		||||
@@ -51,7 +51,7 @@ func MapWUUpdate(wuUpdate *WundergroundUpdate) *weather.WeatherUpdate {
 | 
			
		||||
		StationType:     wuUpdate.SoftwareType,
 | 
			
		||||
		TempOutdoorF:    wuUpdate.Tempf,
 | 
			
		||||
		HumidityOudoor:  wuUpdate.Humidity,
 | 
			
		||||
		WindSpeedMPH:    wuUpdate.WindGustMPH,
 | 
			
		||||
		WindSpeedMPH:    wuUpdate.WindSpeedMPH,
 | 
			
		||||
		WindGustMPH:     wuUpdate.WindGustMPH,
 | 
			
		||||
		WindDir:         wuUpdate.WindDir,
 | 
			
		||||
		UV:              wuUpdate.UV,
 | 
			
		||||
 
 | 
			
		||||
@@ -13,7 +13,7 @@ import (
 | 
			
		||||
 | 
			
		||||
	"gitea.libretechconsulting.com/rmcguire/go-app/pkg/otel"
 | 
			
		||||
 | 
			
		||||
	"gitea.libretechconsulting.com/rmcguire/ambient-weather-local-exporter/pkg/weather"
 | 
			
		||||
	"gitea.libretechconsulting.com/rmcguire/ambient-local-exporter/pkg/weather"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func (wu *WUProvider) ProxyReq(ctx context.Context, update *weather.WeatherUpdate) error {
 | 
			
		||||
 
 | 
			
		||||
@@ -5,21 +5,38 @@ import (
 | 
			
		||||
	"net/url"
 | 
			
		||||
	"strconv"
 | 
			
		||||
 | 
			
		||||
	"gitea.libretechconsulting.com/rmcguire/ambient-weather-local-exporter/pkg/ambient/config"
 | 
			
		||||
	"gitea.libretechconsulting.com/rmcguire/ambient-local-exporter/pkg/ambient/config"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Attempts to complete missing fields that may not
 | 
			
		||||
// be set by a specific provider, such as DewPoint and WindChill
 | 
			
		||||
// TODO: Add span
 | 
			
		||||
func (u *WeatherUpdate) Enrich(weatherStations ...*config.WeatherStation) {
 | 
			
		||||
	if u == nil {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Clear invalid measurements, would be better if these weren't
 | 
			
		||||
	// sent when sensor was out of range.
 | 
			
		||||
	// TODO: This should probably just be done for all fields where
 | 
			
		||||
	// the value is -9999
 | 
			
		||||
	if u.BaromAbsoluteIn != nil && *u.BaromAbsoluteIn < 20 {
 | 
			
		||||
		u.BaromAbsoluteIn = nil
 | 
			
		||||
	}
 | 
			
		||||
	if u.BaromRelativeIn != nil && *u.BaromRelativeIn < 20 {
 | 
			
		||||
		u.BaromRelativeIn = nil
 | 
			
		||||
	}
 | 
			
		||||
	if u.TempIndoorF != nil && *u.TempIndoorF < -1000 {
 | 
			
		||||
		u.TempIndoorF = nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Calculate Wind Chill
 | 
			
		||||
	if u.WindChillF == nil && u.TempOutdoorF != nil && u.WindSpeedMPH != nil {
 | 
			
		||||
		wc := CalculateWindChill(*u.TempOutdoorF, *u.WindSpeedMPH)
 | 
			
		||||
		u.WindChillF = &wc
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Calculate Dew Point
 | 
			
		||||
	if u.DewPointF == nil && (u.TempOutdoorF != nil && u.HumidityOudoor != nil) {
 | 
			
		||||
		if *u.TempOutdoorF != 0 || *u.HumidityOudoor != 0 {
 | 
			
		||||
			dp := CalculateDewPoint(*u.TempOutdoorF, float64(*u.HumidityOudoor))
 | 
			
		||||
@@ -27,11 +44,30 @@ func (u *WeatherUpdate) Enrich(weatherStations ...*config.WeatherStation) {
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Use relative pressure if absolute isn't provided
 | 
			
		||||
	if u.BaromAbsoluteIn == nil && u.BaromRelativeIn != nil {
 | 
			
		||||
		u.BaromAbsoluteIn = u.BaromRelativeIn
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Swaps sensor and component names based on
 | 
			
		||||
// user provided configuration
 | 
			
		||||
func (u *WeatherUpdate) MapSensors() {
 | 
			
		||||
	if u.StationConfig == nil {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Map sensor battery components
 | 
			
		||||
	for i, batt := range u.Batteries {
 | 
			
		||||
		u.Batteries[i].Component = u.StationConfig.MapSensor(batt.Component)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Map other sensors
 | 
			
		||||
	for _, th := range u.TempHumiditySensors {
 | 
			
		||||
		th.Name = u.StationConfig.MapSensor(th.Name)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func CalculateDewPoint(tempF, humidity float64) float64 {
 | 
			
		||||
	// Convert temperature from Fahrenheit to Celsius
 | 
			
		||||
	tempC := (tempF - 32) * 5 / 9
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										13
									
								
								pkg/weather/grpc/weather.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								pkg/weather/grpc/weather.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,13 @@
 | 
			
		||||
package grpc
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"context"
 | 
			
		||||
 | 
			
		||||
	pb "gitea.libretechconsulting.com/rmcguire/ambient-local-exporter/api/v1alpha1/weather"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// TODO: Implement
 | 
			
		||||
type GRPCWeather struct {
 | 
			
		||||
	ctx context.Context
 | 
			
		||||
	*pb.UnimplementedAmbientLocalWeatherServiceServer
 | 
			
		||||
}
 | 
			
		||||
@@ -4,13 +4,13 @@ import (
 | 
			
		||||
	"context"
 | 
			
		||||
 | 
			
		||||
	"gitea.libretechconsulting.com/rmcguire/go-app/pkg/config"
 | 
			
		||||
	"gitea.libretechconsulting.com/rmcguire/go-app/pkg/otel"
 | 
			
		||||
	"github.com/rs/zerolog"
 | 
			
		||||
	"go.opentelemetry.io/otel/attribute"
 | 
			
		||||
	"go.opentelemetry.io/otel/metric"
 | 
			
		||||
	semconv "go.opentelemetry.io/otel/semconv/v1.26.0"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var MetricPrefix = "weather"
 | 
			
		||||
 | 
			
		||||
type WeatherMetrics struct {
 | 
			
		||||
	// Weather Metrics
 | 
			
		||||
	TempOutdoorF    metric.Float64Gauge
 | 
			
		||||
@@ -37,6 +37,10 @@ type WeatherMetrics struct {
 | 
			
		||||
	DewPointF       metric.Float64Gauge
 | 
			
		||||
	WindChillF      metric.Float64Gauge
 | 
			
		||||
 | 
			
		||||
	// Temp and Humidity Sensors
 | 
			
		||||
	SensorTempF    metric.Float64Gauge
 | 
			
		||||
	SensorHumidity metric.Int64Gauge
 | 
			
		||||
 | 
			
		||||
	// Internal Telemetry
 | 
			
		||||
	UpdatesReceived metric.Int64Counter
 | 
			
		||||
	appCtx          context.Context
 | 
			
		||||
@@ -45,90 +49,8 @@ type WeatherMetrics struct {
 | 
			
		||||
	recorder        *MetricRecorder
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var MetricPrefix = "weather"
 | 
			
		||||
 | 
			
		||||
func MustInitMetrics(appCtx context.Context) *WeatherMetrics {
 | 
			
		||||
	wm := &WeatherMetrics{
 | 
			
		||||
		appCtx:   appCtx,
 | 
			
		||||
		cfg:      config.MustFromCtx(appCtx),
 | 
			
		||||
		recorder: &MetricRecorder{ctx: appCtx, l: zerolog.Ctx(appCtx)},
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	wm.meter = otel.GetMeter(appCtx, "weather", "metrics")
 | 
			
		||||
 | 
			
		||||
	// Weather Metrics
 | 
			
		||||
	wm.TempOutdoorF, _ = wm.meter.Float64Gauge(MetricPrefix+"_temp_outdoor_f",
 | 
			
		||||
		metric.WithDescription("Outdoor Temperature in Faherenheit"))
 | 
			
		||||
	wm.TempIndoorF, _ = wm.meter.Float64Gauge(MetricPrefix+"_temp_indoor_f",
 | 
			
		||||
		metric.WithDescription("Indoor Temperature in Faherenheit"))
 | 
			
		||||
	wm.HumidityOudoor, _ = wm.meter.Int64Gauge(MetricPrefix+"_humidity_outdoor",
 | 
			
		||||
		metric.WithDescription("Outdoor Humidity %"))
 | 
			
		||||
	wm.HumidityIndoor, _ = wm.meter.Int64Gauge(MetricPrefix+"_humidity_indoor",
 | 
			
		||||
		metric.WithDescription("Indoor Humidity %"))
 | 
			
		||||
	wm.WindSpeedMPH, _ = wm.meter.Float64Gauge(MetricPrefix+"_wind_speed_mph",
 | 
			
		||||
		metric.WithDescription("Wind Speed in MPH"))
 | 
			
		||||
	wm.WindGustMPH, _ = wm.meter.Float64Gauge(MetricPrefix+"_wind_gust_mph",
 | 
			
		||||
		metric.WithDescription("Wind Gust in MPH"))
 | 
			
		||||
	wm.MaxDailyGust, _ = wm.meter.Float64Gauge(MetricPrefix+"_max_daily_gust",
 | 
			
		||||
		metric.WithDescription("Max Daily Wind Gust"))
 | 
			
		||||
	wm.WindDir, _ = wm.meter.Int64Gauge(MetricPrefix+"_wind_dir",
 | 
			
		||||
		metric.WithDescription("Wind Direction in Degrees"))
 | 
			
		||||
	wm.WindDirAvg10m, _ = wm.meter.Int64Gauge(MetricPrefix+"_wind_dir_avg_10m",
 | 
			
		||||
		metric.WithDescription("Wind Direction 10m Average"))
 | 
			
		||||
	wm.UV, _ = wm.meter.Int64Gauge(MetricPrefix+"_uv",
 | 
			
		||||
		metric.WithDescription("UV Index"))
 | 
			
		||||
	wm.SolarRadiation, _ = wm.meter.Float64Gauge(MetricPrefix+"_solar_radiation",
 | 
			
		||||
		metric.WithDescription("Solar Radiation in W/㎡"))
 | 
			
		||||
	wm.HourlyRainIn, _ = wm.meter.Float64Gauge(MetricPrefix+"_hourly_rain_in",
 | 
			
		||||
		metric.WithDescription("Hourly Rain in Inches"))
 | 
			
		||||
	wm.EventRainIn, _ = wm.meter.Float64Gauge(MetricPrefix+"_event_rain_in",
 | 
			
		||||
		metric.WithDescription("Event Rain in Inches"))
 | 
			
		||||
	wm.DailyRainIn, _ = wm.meter.Float64Gauge(MetricPrefix+"_daily_rain_in",
 | 
			
		||||
		metric.WithDescription("Daily Rain in Inches"))
 | 
			
		||||
	wm.WeeklyRainIn, _ = wm.meter.Float64Gauge(MetricPrefix+"_weekly_rain_in",
 | 
			
		||||
		metric.WithDescription("Weekly Rain in Inches"))
 | 
			
		||||
	wm.MonthlyRainIn, _ = wm.meter.Float64Gauge(MetricPrefix+"_monthly_rain_in",
 | 
			
		||||
		metric.WithDescription("Monthly Rain in Inches"))
 | 
			
		||||
	wm.YearlyRainIn, _ = wm.meter.Float64Gauge(MetricPrefix+"_yearly_rain_in",
 | 
			
		||||
		metric.WithDescription("Yearly Rain in Inches"))
 | 
			
		||||
	wm.TotalRainIn, _ = wm.meter.Float64Gauge(MetricPrefix+"_total_rain_in",
 | 
			
		||||
		metric.WithDescription("Total Rain in Inches"))
 | 
			
		||||
	wm.BatteryStatus, _ = wm.meter.Int64Gauge(MetricPrefix+"_battery_status",
 | 
			
		||||
		metric.WithDescription("Per-component battery status"))
 | 
			
		||||
	wm.BaromRelativeIn, _ = wm.meter.Float64Gauge(MetricPrefix+"_barometric_pressure_relative_in",
 | 
			
		||||
		metric.WithDescription("Relative Pressure in Inches of Mercury"))
 | 
			
		||||
	wm.BaromAbsoluteIn, _ = wm.meter.Float64Gauge(MetricPrefix+"_barometric_pressure_absolute_in",
 | 
			
		||||
		metric.WithDescription("Absolute Pressure in Inches of Mercury"))
 | 
			
		||||
	wm.DewPointF, _ = wm.meter.Float64Gauge(MetricPrefix+"_dew_point_f",
 | 
			
		||||
		metric.WithDescription("Dew Point in Faherenheit"))
 | 
			
		||||
	wm.WindChillF, _ = wm.meter.Float64Gauge(MetricPrefix+"_wind_chill_f",
 | 
			
		||||
		metric.WithDescription("Wind Chill in Faherenheit"))
 | 
			
		||||
 | 
			
		||||
	// Internal Telemetry
 | 
			
		||||
	wm.UpdatesReceived, _ = wm.meter.Int64Counter(MetricPrefix+"_updates_received",
 | 
			
		||||
		metric.WithDescription("Metric Updates Processed by Exporter"))
 | 
			
		||||
 | 
			
		||||
	return wm
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (wm *WeatherMetrics) Update(u *WeatherUpdate) {
 | 
			
		||||
	attributes := []attribute.KeyValue{
 | 
			
		||||
		semconv.ServiceVersion(wm.cfg.Version),
 | 
			
		||||
	}
 | 
			
		||||
	if u.StationType != nil {
 | 
			
		||||
		attributes = append(attributes,
 | 
			
		||||
			attribute.String("station_type", *u.StationType))
 | 
			
		||||
	}
 | 
			
		||||
	if u.StationConfig != nil {
 | 
			
		||||
		if u.StationConfig.Name != "" {
 | 
			
		||||
			attributes = append(attributes,
 | 
			
		||||
				attribute.String("station_name", u.StationConfig.Name))
 | 
			
		||||
		}
 | 
			
		||||
		if u.StationConfig.Equipment != "" {
 | 
			
		||||
			attributes = append(attributes,
 | 
			
		||||
				attribute.String("station_equipment", u.StationConfig.Equipment))
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	attributes := wm.GetAttributes(u)
 | 
			
		||||
 | 
			
		||||
	wm.recorder.Record(&RecordOpts{Float64Gauge: wm.TempOutdoorF, FloatVal: u.TempOutdoorF, Field: FieldTempOutdoorF, Attributes: attributes, Station: u.StationConfig})
 | 
			
		||||
	wm.recorder.Record(&RecordOpts{Float64Gauge: wm.TempIndoorF, FloatVal: u.TempIndoorF, Field: FieldTempIndoorF, Attributes: attributes, Station: u.StationConfig})
 | 
			
		||||
@@ -153,13 +75,73 @@ func (wm *WeatherMetrics) Update(u *WeatherUpdate) {
 | 
			
		||||
	wm.recorder.Record(&RecordOpts{Float64Gauge: wm.DewPointF, FloatVal: u.DewPointF, Field: FieldDewPointF, Attributes: attributes, Station: u.StationConfig})
 | 
			
		||||
	wm.recorder.Record(&RecordOpts{Float64Gauge: wm.WindChillF, FloatVal: u.WindChillF, Field: FieldWindChillF, Attributes: attributes, Station: u.StationConfig})
 | 
			
		||||
 | 
			
		||||
	// Batteries
 | 
			
		||||
	for _, battery := range u.Batteries {
 | 
			
		||||
		batAttr := attributes
 | 
			
		||||
		batAttr = append(batAttr, attribute.String("component", battery.Component))
 | 
			
		||||
 | 
			
		||||
		wm.recorder.Record(&RecordOpts{Int64Gauge: wm.BatteryStatus, IntVal: battery.Status, Field: FieldBatteries, Attributes: batAttr, Station: u.StationConfig})
 | 
			
		||||
	}
 | 
			
		||||
	wm.RecordBatteries(u, attributes)
 | 
			
		||||
	wm.RecordTempHumiditySensors(u, attributes)
 | 
			
		||||
 | 
			
		||||
	wm.UpdatesReceived.Add(wm.appCtx, 1)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (wm *WeatherMetrics) RecordBatteries(u *WeatherUpdate, attr []attribute.KeyValue) {
 | 
			
		||||
	for _, battery := range u.Batteries {
 | 
			
		||||
		batAttr := attr
 | 
			
		||||
		batAttr = append(batAttr, attribute.String("component", battery.Component))
 | 
			
		||||
 | 
			
		||||
		wm.recorder.Record(&RecordOpts{
 | 
			
		||||
			Int64Gauge: wm.BatteryStatus,
 | 
			
		||||
			IntVal:     battery.Status,
 | 
			
		||||
			Field:      FieldBatteries,
 | 
			
		||||
			Attributes: batAttr,
 | 
			
		||||
			Station:    u.StationConfig,
 | 
			
		||||
		})
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (wm *WeatherMetrics) RecordTempHumiditySensors(u *WeatherUpdate, attr []attribute.KeyValue) {
 | 
			
		||||
	if u == nil || u.TempHumiditySensors == nil {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for _, sensor := range u.TempHumiditySensors {
 | 
			
		||||
		sensorAttr := attr
 | 
			
		||||
		sensorAttr = append(sensorAttr, attribute.String("sensorName", sensor.Name))
 | 
			
		||||
 | 
			
		||||
		wm.recorder.Record(&RecordOpts{
 | 
			
		||||
			Float64Gauge: wm.SensorTempF,
 | 
			
		||||
			FloatVal:     sensor.TempF,
 | 
			
		||||
			Field:        FieldSensorTempF,
 | 
			
		||||
			Attributes:   sensorAttr,
 | 
			
		||||
			Station:      u.StationConfig,
 | 
			
		||||
		})
 | 
			
		||||
		wm.recorder.Record(&RecordOpts{
 | 
			
		||||
			Int64Gauge: wm.SensorHumidity,
 | 
			
		||||
			IntVal:     sensor.Humidity,
 | 
			
		||||
			Field:      FieldSensorHumidity,
 | 
			
		||||
			Attributes: sensorAttr,
 | 
			
		||||
			Station:    u.StationConfig,
 | 
			
		||||
		})
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (wm *WeatherMetrics) GetAttributes(u *WeatherUpdate) []attribute.KeyValue {
 | 
			
		||||
	attributes := []attribute.KeyValue{
 | 
			
		||||
		semconv.ServiceVersion(wm.cfg.Version),
 | 
			
		||||
		semconv.ServiceName(wm.cfg.Name),
 | 
			
		||||
		semconv.DeploymentEnvironment(wm.cfg.Environment),
 | 
			
		||||
	}
 | 
			
		||||
	if u.StationType != nil {
 | 
			
		||||
		attributes = append(attributes,
 | 
			
		||||
			attribute.String("station_type", *u.StationType))
 | 
			
		||||
	}
 | 
			
		||||
	if u.StationConfig != nil {
 | 
			
		||||
		if u.StationConfig.Name != "" {
 | 
			
		||||
			attributes = append(attributes,
 | 
			
		||||
				attribute.String("station_name", u.StationConfig.Name))
 | 
			
		||||
		}
 | 
			
		||||
		if u.StationConfig.Equipment != "" {
 | 
			
		||||
			attributes = append(attributes,
 | 
			
		||||
				attribute.String("station_equipment", u.StationConfig.Equipment))
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return attributes
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										80
									
								
								pkg/weather/metrics_init.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										80
									
								
								pkg/weather/metrics_init.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,80 @@
 | 
			
		||||
package weather
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"context"
 | 
			
		||||
 | 
			
		||||
	"gitea.libretechconsulting.com/rmcguire/go-app/pkg/config"
 | 
			
		||||
	"gitea.libretechconsulting.com/rmcguire/go-app/pkg/otel"
 | 
			
		||||
	"github.com/rs/zerolog"
 | 
			
		||||
	"go.opentelemetry.io/otel/metric"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func MustInitMetrics(appCtx context.Context) *WeatherMetrics {
 | 
			
		||||
	wm := &WeatherMetrics{
 | 
			
		||||
		appCtx:   appCtx,
 | 
			
		||||
		cfg:      config.MustFromCtx(appCtx),
 | 
			
		||||
		recorder: &MetricRecorder{ctx: appCtx, l: zerolog.Ctx(appCtx)},
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	wm.meter = otel.GetMeter(appCtx, "weather", "metrics")
 | 
			
		||||
 | 
			
		||||
	// Weather Metrics
 | 
			
		||||
	wm.TempOutdoorF, _ = wm.meter.Float64Gauge(MetricPrefix+"_temp_outdoor_f",
 | 
			
		||||
		metric.WithDescription("Outdoor Temperature in Faherenheit"))
 | 
			
		||||
	wm.TempIndoorF, _ = wm.meter.Float64Gauge(MetricPrefix+"_temp_indoor_f",
 | 
			
		||||
		metric.WithDescription("Indoor Temperature in Faherenheit"))
 | 
			
		||||
	wm.HumidityOudoor, _ = wm.meter.Int64Gauge(MetricPrefix+"_humidity_outdoor",
 | 
			
		||||
		metric.WithDescription("Outdoor Humidity %"))
 | 
			
		||||
	wm.HumidityIndoor, _ = wm.meter.Int64Gauge(MetricPrefix+"_humidity_indoor",
 | 
			
		||||
		metric.WithDescription("Indoor Humidity %"))
 | 
			
		||||
	wm.WindSpeedMPH, _ = wm.meter.Float64Gauge(MetricPrefix+"_wind_speed_mph",
 | 
			
		||||
		metric.WithDescription("Wind Speed in MPH"))
 | 
			
		||||
	wm.WindGustMPH, _ = wm.meter.Float64Gauge(MetricPrefix+"_wind_gust_mph",
 | 
			
		||||
		metric.WithDescription("Wind Gust in MPH"))
 | 
			
		||||
	wm.MaxDailyGust, _ = wm.meter.Float64Gauge(MetricPrefix+"_max_daily_gust",
 | 
			
		||||
		metric.WithDescription("Max Daily Wind Gust"))
 | 
			
		||||
	wm.WindDir, _ = wm.meter.Int64Gauge(MetricPrefix+"_wind_dir",
 | 
			
		||||
		metric.WithDescription("Wind Direction in Degrees"))
 | 
			
		||||
	wm.WindDirAvg10m, _ = wm.meter.Int64Gauge(MetricPrefix+"_wind_dir_avg_10m",
 | 
			
		||||
		metric.WithDescription("Wind Direction 10m Average"))
 | 
			
		||||
	wm.UV, _ = wm.meter.Int64Gauge(MetricPrefix+"_uv",
 | 
			
		||||
		metric.WithDescription("UV Index"))
 | 
			
		||||
	wm.SolarRadiation, _ = wm.meter.Float64Gauge(MetricPrefix+"_solar_radiation",
 | 
			
		||||
		metric.WithDescription("Solar Radiation in W/㎡"))
 | 
			
		||||
	wm.HourlyRainIn, _ = wm.meter.Float64Gauge(MetricPrefix+"_hourly_rain_in",
 | 
			
		||||
		metric.WithDescription("Hourly Rain in Inches"))
 | 
			
		||||
	wm.EventRainIn, _ = wm.meter.Float64Gauge(MetricPrefix+"_event_rain_in",
 | 
			
		||||
		metric.WithDescription("Event Rain in Inches"))
 | 
			
		||||
	wm.DailyRainIn, _ = wm.meter.Float64Gauge(MetricPrefix+"_daily_rain_in",
 | 
			
		||||
		metric.WithDescription("Daily Rain in Inches"))
 | 
			
		||||
	wm.WeeklyRainIn, _ = wm.meter.Float64Gauge(MetricPrefix+"_weekly_rain_in",
 | 
			
		||||
		metric.WithDescription("Weekly Rain in Inches"))
 | 
			
		||||
	wm.MonthlyRainIn, _ = wm.meter.Float64Gauge(MetricPrefix+"_monthly_rain_in",
 | 
			
		||||
		metric.WithDescription("Monthly Rain in Inches"))
 | 
			
		||||
	wm.YearlyRainIn, _ = wm.meter.Float64Gauge(MetricPrefix+"_yearly_rain_in",
 | 
			
		||||
		metric.WithDescription("Yearly Rain in Inches"))
 | 
			
		||||
	wm.TotalRainIn, _ = wm.meter.Float64Gauge(MetricPrefix+"_total_rain_in",
 | 
			
		||||
		metric.WithDescription("Total Rain in Inches"))
 | 
			
		||||
	wm.BatteryStatus, _ = wm.meter.Int64Gauge(MetricPrefix+"_battery_status",
 | 
			
		||||
		metric.WithDescription("Per-component battery status"))
 | 
			
		||||
	wm.BaromRelativeIn, _ = wm.meter.Float64Gauge(MetricPrefix+"_barometric_pressure_relative_in",
 | 
			
		||||
		metric.WithDescription("Relative Pressure in Inches of Mercury"))
 | 
			
		||||
	wm.BaromAbsoluteIn, _ = wm.meter.Float64Gauge(MetricPrefix+"_barometric_pressure_absolute_in",
 | 
			
		||||
		metric.WithDescription("Absolute Pressure in Inches of Mercury"))
 | 
			
		||||
	wm.DewPointF, _ = wm.meter.Float64Gauge(MetricPrefix+"_dew_point_f",
 | 
			
		||||
		metric.WithDescription("Dew Point in Faherenheit"))
 | 
			
		||||
	wm.WindChillF, _ = wm.meter.Float64Gauge(MetricPrefix+"_wind_chill_f",
 | 
			
		||||
		metric.WithDescription("Wind Chill in Faherenheit"))
 | 
			
		||||
 | 
			
		||||
	// Temp and Humidity Sensors
 | 
			
		||||
	wm.SensorTempF, _ = wm.meter.Float64Gauge(MetricPrefix+"_sensor_temp_f",
 | 
			
		||||
		metric.WithDescription("Temperature Sensor in Faherenheit"))
 | 
			
		||||
	wm.SensorHumidity, _ = wm.meter.Int64Gauge(MetricPrefix+"_sensor_humidity",
 | 
			
		||||
		metric.WithDescription("Humidity % Sensor"))
 | 
			
		||||
 | 
			
		||||
	// Internal Telemetry
 | 
			
		||||
	wm.UpdatesReceived, _ = wm.meter.Int64Counter(MetricPrefix+"_updates_received",
 | 
			
		||||
		metric.WithDescription("Metric Updates Processed by Exporter"))
 | 
			
		||||
 | 
			
		||||
	return wm
 | 
			
		||||
}
 | 
			
		||||
@@ -8,7 +8,7 @@ import (
 | 
			
		||||
	"go.opentelemetry.io/otel/attribute"
 | 
			
		||||
	"go.opentelemetry.io/otel/metric"
 | 
			
		||||
 | 
			
		||||
	"gitea.libretechconsulting.com/rmcguire/ambient-weather-local-exporter/pkg/ambient/config"
 | 
			
		||||
	"gitea.libretechconsulting.com/rmcguire/ambient-local-exporter/pkg/ambient/config"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type MetricRecorder struct {
 | 
			
		||||
 
 | 
			
		||||
@@ -3,7 +3,7 @@ package weather
 | 
			
		||||
import (
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	"gitea.libretechconsulting.com/rmcguire/ambient-weather-local-exporter/pkg/ambient/config"
 | 
			
		||||
	"gitea.libretechconsulting.com/rmcguire/ambient-local-exporter/pkg/ambient/config"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Stable intermediate struct containing superset of fields
 | 
			
		||||
@@ -38,8 +38,14 @@ type WeatherUpdate struct {
 | 
			
		||||
	// if not otherwise set
 | 
			
		||||
	DewPointF  *float64
 | 
			
		||||
	WindChillF *float64
 | 
			
		||||
	// First URL parameters given to AWN/Wunderground
 | 
			
		||||
	// if proxying is enabled
 | 
			
		||||
	// Extra Temp+Humidity Sensors
 | 
			
		||||
	TempHumiditySensors []*TempHumiditySensor
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type TempHumiditySensor struct {
 | 
			
		||||
	Name     string
 | 
			
		||||
	TempF    *float64
 | 
			
		||||
	Humidity *int
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type BatteryStatus struct {
 | 
			
		||||
@@ -76,6 +82,8 @@ const (
 | 
			
		||||
	FieldBaromAbsoluteIn = "BaromAbsoluteIn"
 | 
			
		||||
	FieldDewPointF       = "DewPointF"
 | 
			
		||||
	FieldWindChillF      = "WindChillF"
 | 
			
		||||
	FieldSensorTempF     = "SensorTempF"
 | 
			
		||||
	FieldSensorHumidity  = "SensorHumidity"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func (u *WeatherUpdate) GetStationName() string {
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										63
									
								
								proto/weather/weather.proto
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										63
									
								
								proto/weather/weather.proto
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,63 @@
 | 
			
		||||
syntax = "proto3";
 | 
			
		||||
package ambient.weather;
 | 
			
		||||
 | 
			
		||||
import "google/protobuf/timestamp.proto";
 | 
			
		||||
 | 
			
		||||
option go_package = "gitea.libretechconsulting.com/rmcguire/ambient-local-exporter/api/v1alpha1/weather";
 | 
			
		||||
 | 
			
		||||
message GetWeatherRequest {
 | 
			
		||||
	GetWeatherOpts opts = 1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
message GetWeatherResponse{
 | 
			
		||||
	google.protobuf.Timestamp last_updated = 1;
 | 
			
		||||
	repeated WeatherUpdate weather_updates = 2;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
message GetWeatherOpts {
 | 
			
		||||
	optional string station_name = 1;
 | 
			
		||||
	optional string station_type = 2;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
message WeatherUpdate {
 | 
			
		||||
	string station_name = 1;
 | 
			
		||||
	string station_type = 2;
 | 
			
		||||
	string station_id = 3;
 | 
			
		||||
  optional double temp_outdoor_f = 4;
 | 
			
		||||
  optional double temp_indoor_f = 5;
 | 
			
		||||
  optional int32 humidity_outdoor = 6;
 | 
			
		||||
  optional int32 humidity_indoor = 7;
 | 
			
		||||
  optional double wind_speed_mph = 8;
 | 
			
		||||
  optional double wind_gust_mph = 9;
 | 
			
		||||
  optional double max_daily_gust = 10;
 | 
			
		||||
  optional int32 wind_dir = 11;
 | 
			
		||||
  optional int32 wind_dir_avg_10m = 12;
 | 
			
		||||
  optional int32 uv = 13;
 | 
			
		||||
  optional double solar_radiation = 14;
 | 
			
		||||
  optional double hourly_rain_in = 15;
 | 
			
		||||
  optional double event_rain_in = 16;
 | 
			
		||||
  optional double daily_rain_in = 17;
 | 
			
		||||
  optional double weekly_rain_in = 18;
 | 
			
		||||
  optional double monthly_rain_in = 19;
 | 
			
		||||
  optional double yearly_rain_in = 20;
 | 
			
		||||
  optional double total_rain_in = 21;
 | 
			
		||||
  repeated BatteryStatus batteries = 22;
 | 
			
		||||
  optional double barom_relative_in = 23;
 | 
			
		||||
  optional double barom_absolute_in = 24;
 | 
			
		||||
  optional double dew_point_f = 25;
 | 
			
		||||
  optional double wind_chill_f = 26;
 | 
			
		||||
  repeated TempHumiditySensor temp_humidity_sensors = 27;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Represents a temperature and humidity sensor
 | 
			
		||||
message TempHumiditySensor {
 | 
			
		||||
  string name = 1;
 | 
			
		||||
  optional double temp_f = 2;
 | 
			
		||||
  optional int32 humidity = 3;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Represents battery status for different components
 | 
			
		||||
message BatteryStatus {
 | 
			
		||||
  string component = 1;
 | 
			
		||||
  optional int32 status = 2;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										10
									
								
								proto/weather/weather_service.proto
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								proto/weather/weather_service.proto
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,10 @@
 | 
			
		||||
syntax = "proto3";
 | 
			
		||||
package ambient.weather;
 | 
			
		||||
 | 
			
		||||
import "weather/weather.proto";
 | 
			
		||||
 | 
			
		||||
option go_package = "gitea.libretechconsulting.com/rmcguire/ambient-local-exporter/api/v1alpha1/weather";
 | 
			
		||||
 | 
			
		||||
service AmbientLocalWeatherService {
 | 
			
		||||
  rpc GetWeather(GetWeatherRequest) returns (GetWeatherResponse);
 | 
			
		||||
}
 | 
			
		||||
		Reference in New Issue
	
	Block a user