All checks were successful
CI / changes (push) Successful in 19s
CI / test (push) Successful in 24s
CI / build-ai-gateway (push) Successful in 1m6s
CI / build-ha-gateway (push) Successful in 1m3s
CI / build-discord-bot (push) Successful in 1m4s
CI / build-alexa-bridge (push) Successful in 1m15s
CI / build-tts-gateway (push) Successful in 1m5s
CI / build-tts-sidecar (push) Has been skipped
CI / build-tts-model (push) Has been skipped
- Implemented SetTemperature in ClimateService for setting an absolute target temperature. - Updated ClimateServiceClient and ClimateServiceServer interfaces to include SetTemperature. - Added corresponding handler and tests for SetTemperature in ClimateGRPC. - Modified ClimateApp to handle SetTemperature requests without clamping. - Updated climate.proto to define SetTemperatureRequest message. - Adjusted Dockerfiles to include alexa-bridge dependencies.
63 lines
1.7 KiB
Go
63 lines
1.7 KiB
Go
package entities
|
|
|
|
import (
|
|
"context"
|
|
"log/slog"
|
|
"time"
|
|
)
|
|
|
|
// Refresher fetches entities from ha-gateway on startup (blocking, fail
|
|
// loud — without an initial entity list nothing can resolve) and then
|
|
// periodically in the background (non-blocking; a failed periodic refresh
|
|
// only logs and keeps serving the last-known-good snapshot, since going
|
|
// down over a transient ha-gateway hiccup would be worse than serving
|
|
// slightly stale entity names).
|
|
type Refresher struct {
|
|
client *Client
|
|
resolver *Resolver
|
|
interval time.Duration
|
|
log *slog.Logger
|
|
}
|
|
|
|
// NewRefresher constructs a Refresher. client fetches entities, resolver is
|
|
// the snapshot it populates, interval controls the periodic refresh cadence.
|
|
func NewRefresher(client *Client, resolver *Resolver, interval time.Duration, log *slog.Logger) *Refresher {
|
|
return &Refresher{client: client, resolver: resolver, interval: interval, log: log}
|
|
}
|
|
|
|
// Start performs the blocking initial fetch, then launches a background
|
|
// goroutine for periodic refreshes until ctx is cancelled. It returns an
|
|
// error only from the initial fetch — the caller should fail startup on it.
|
|
func (r *Refresher) Start(ctx context.Context) error {
|
|
if err := r.refreshOnce(ctx); err != nil {
|
|
return err
|
|
}
|
|
|
|
go r.loop(ctx)
|
|
return nil
|
|
}
|
|
|
|
func (r *Refresher) refreshOnce(ctx context.Context) error {
|
|
list, err := r.client.FetchAll(ctx)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
r.resolver.Replace(list)
|
|
return nil
|
|
}
|
|
|
|
func (r *Refresher) loop(ctx context.Context) {
|
|
ticker := time.NewTicker(r.interval)
|
|
defer ticker.Stop()
|
|
for {
|
|
select {
|
|
case <-ctx.Done():
|
|
return
|
|
case <-ticker.C:
|
|
if err := r.refreshOnce(ctx); err != nil {
|
|
r.log.Error("periodic entity refresh failed, keeping stale data", "err", err)
|
|
}
|
|
}
|
|
}
|
|
}
|