Files
econet-exporter/pkg/econet/econet.go
T
rmcguire b2ec72352a
Build and Publish / go-binaries (push) Has been skipped
Build and Publish / container-images (push) Has been skipped
Build and Publish / helm-release (push) Successful in 19s
Build and Publish / check-chart (push) Successful in 52s
implement econet exporter
2026-07-04 17:22:31 -04:00

95 lines
2.7 KiB
Go

// Package econet provides EconetService, a service.AppService that connects
// to the Rheem EcoNet cloud and exposes device state via gRPC, an MCP tool,
// and OTEL metrics served for Prometheus scraping.
package econet
import (
"context"
"fmt"
"log/slog"
rheemcloud "github.com/kevinburke/rheemcloud-go"
optsgrpc "gitea.libretechconsulting.com/rmcguire/go-app/pkg/srv/grpc/opts"
optshttp "gitea.libretechconsulting.com/rmcguire/go-app/pkg/srv/http/opts"
"gitea.libretechconsulting.com/rmcguire/econet-exporter/pkg/config"
"gitea.libretechconsulting.com/rmcguire/econet-exporter/pkg/econet/econetgrpc"
"gitea.libretechconsulting.com/rmcguire/econet-exporter/pkg/econet/econetmcp"
"gitea.libretechconsulting.com/rmcguire/econet-exporter/pkg/econet/econetmetrics"
"gitea.libretechconsulting.com/rmcguire/econet-exporter/pkg/service"
)
type EconetService struct {
ctx context.Context
config *config.ServiceConfig
client *rheemcloud.Client
grpc *econetgrpc.EconetGRPCServer
mcp *econetmcp.EconetMCPServer
metrics *econetmetrics.Collector
}
func (e *EconetService) Init(ctx context.Context, cfg *config.ServiceConfig) (service.ShutdownFunc, error) {
e.ctx = ctx
e.config = cfg
client, err := connect(ctx, cfg)
if err != nil {
return nil, err
}
e.client = client
e.grpc = econetgrpc.NewEconetGRPCServer(ctx, cfg, client)
e.mcp = econetmcp.NewEconetMCPServer(ctx, cfg, e.grpc)
e.metrics = econetmetrics.NewCollector(ctx, cfg, client)
if err := e.metrics.Start(); err != nil {
return nil, err
}
return e.shutdown, nil
}
func connect(ctx context.Context, cfg *config.ServiceConfig) (*rheemcloud.Client, error) {
if cfg.EconetEmail == "" || cfg.EconetPassword == "" {
return nil, fmt.Errorf("econet: email and password required (set ECONET_EMAIL / ECONET_PASSWORD)")
}
client, err := rheemcloud.Connect(ctx, cfg.EconetEmail, cfg.EconetPassword, &rheemcloud.Config{
Logger: slog.Default(),
})
if err != nil {
return nil, fmt.Errorf("econet: connect failed: %w", err)
}
return client, nil
}
func (e *EconetService) shutdown(_ context.Context) (string, error) {
e.metrics.Stop()
return "EconetService", e.client.Close()
}
func (e *EconetService) GetGRPC() *optsgrpc.AppGRPC {
return &optsgrpc.AppGRPC{
Services: e.grpc.GetServices(),
GRPCDialOpts: e.grpc.GetDialOpts(),
}
}
func (e *EconetService) GetHTTP() *optshttp.AppHTTP {
return &optshttp.AppHTTP{
Ctx: e.ctx,
Handlers: e.mcp.GetHandlers(),
HealthChecks: e.healthChecks(),
}
}
func (e *EconetService) healthChecks() []optshttp.HealthCheckFunc {
return []optshttp.HealthCheckFunc{
func(_ context.Context) error {
if len(e.client.Devices()) == 0 {
return fmt.Errorf("econet: no devices loaded")
}
return nil
},
}
}