remove rheemcloud pkg, implement against rest api only
Build and Publish / helm-release (push) Has been skipped
Build and Publish / go-binaries (push) Has been skipped
Build and Publish / container-images (push) Has been skipped
Build and Publish / check-chart (push) Successful in 12s

This commit is contained in:
2026-07-05 16:59:53 -04:00
parent dcdf5e4230
commit 047f7c233d
15 changed files with 822 additions and 233 deletions
+43 -31
View File
@@ -5,17 +5,16 @@ package econet
import (
"context"
"crypto/tls"
"fmt"
"log/slog"
"net/http"
"time"
rheemcloud "github.com/kevinburke/rheemcloud-go"
"github.com/rs/zerolog"
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/econetclient"
"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"
@@ -25,7 +24,8 @@ import (
type EconetService struct {
ctx context.Context
config *config.ServiceConfig
client *rheemcloud.Client
log *zerolog.Logger
client *econetclient.Client
grpc *econetgrpc.EconetGRPCServer
mcp *econetmcp.EconetMCPServer
metrics *econetmetrics.Collector
@@ -34,46 +34,58 @@ type EconetService struct {
func (e *EconetService) Init(ctx context.Context, cfg *config.ServiceConfig) (service.ShutdownFunc, error) {
e.ctx = ctx
e.config = cfg
e.log = zerolog.Ctx(ctx)
client, err := connect(ctx, cfg)
if err != nil {
return nil, err
// Fail fast on missing credentials (cheap, no network).
if cfg.EconetEmail == "" || cfg.EconetPassword == "" {
return nil, fmt.Errorf("econet: email and password required (set ECONET_EMAIL / ECONET_PASSWORD)")
}
e.client = client
e.grpc = econetgrpc.NewEconetGRPCServer(ctx, cfg, client)
e.client = econetclient.New(cfg, e.log)
e.grpc = econetgrpc.NewEconetGRPCServer(ctx, cfg, e.client)
e.mcp = econetmcp.NewEconetMCPServer(ctx, cfg, e.grpc)
e.metrics = econetmetrics.NewCollector(ctx, cfg, client)
if err := e.metrics.Start(); err != nil {
e.metrics = econetmetrics.NewCollector(ctx, cfg, e.client)
if err := e.metrics.RegisterGauges(); err != nil {
return nil, err
}
// Poll the cloud in the background so a slow or unreachable Rheem cloud
// never blocks HTTP/gRPC server startup. Devices are empty until the first
// successful refresh; the gauges and gRPC handlers tolerate that.
go e.pollLoop(ctx)
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)")
// pollLoop refreshes device state on a ticker for the life of the service.
// A per-refresh timeout keeps a hung REST call from stalling the loop.
func (e *EconetService) pollLoop(ctx context.Context) {
interval := e.config.GetPollInterval()
e.refresh(ctx)
t := time.NewTicker(interval)
defer t.Stop()
for {
select {
case <-ctx.Done():
return
case <-t.C:
e.refresh(ctx)
}
}
client, err := rheemcloud.Connect(ctx, cfg.EconetEmail, cfg.EconetPassword, &rheemcloud.Config{
Logger: slog.Default(),
HTTPClient: &http.Client{
Transport: &http.Transport{
TLSClientConfig: &tls.Config{
InsecureSkipVerify: cfg.EconetTLSInsecure,
},
},
},
})
if err != nil {
return nil, fmt.Errorf("econet: connect failed: %w", err)
}
func (e *EconetService) refresh(ctx context.Context) {
rctx, cancel := context.WithTimeout(ctx, e.config.GetPollInterval())
defer cancel()
if err := e.client.Refresh(rctx); err != nil {
e.log.Error().Err(err).Msg("econet: refresh failed")
return
}
return client, nil
e.log.Debug().Int("devices", len(e.client.Devices())).Msg("econet: refreshed")
}
func (e *EconetService) shutdown(_ context.Context) (string, error) {
e.metrics.Stop()
return "EconetService", e.client.Close()
return "EconetService", nil
}
func (e *EconetService) GetGRPC() *optsgrpc.AppGRPC {
@@ -95,7 +107,7 @@ 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 fmt.Errorf("econet: no devices loaded yet")
}
return nil
},