evcc-io/plugin/cached.go
2026-08-20 13:36:53 +02:00

99 lines
2 KiB
Go

package plugin
import (
"context"
"errors"
"fmt"
"sync"
"time"
"github.com/benbjohnson/clock"
"github.com/evcc-io/evcc/util"
)
func init() {
registry.AddCtx("cached", NewCachedFromConfig)
}
// cachedPlugin caches the wrapped reading for the configured duration
type cachedPlugin struct {
ctx context.Context
clock clock.Clock
cache time.Duration
value Config
}
// NewCachedFromConfig creates cached provider
func NewCachedFromConfig(ctx context.Context, other map[string]any) (Plugin, error) {
var cc struct {
Cache time.Duration
Value Config
}
if err := util.DecodeOther(other, &cc); err != nil {
return nil, err
}
if cc.Cache <= 0 {
return nil, errors.New("cache duration is required")
}
return &cachedPlugin{
ctx: ctx,
clock: clock.New(),
cache: cc.Cache,
value: cc.Value,
}, nil
}
func (o *cachedPlugin) cachedGetter[T any](valuer func(ctx context.Context) (func() (T, error), error)) (func() (T, error), error) {
value, err := valuer(o.ctx)
if err != nil {
return nil, fmt.Errorf("cached: %w", err)
}
var mu sync.Mutex
var updated time.Time
var val T
return func() (T, error) {
mu.Lock()
defer mu.Unlock()
// refresh on first call and once the cache has expired; failures are not cached
if updated.IsZero() || o.clock.Since(updated) > o.cache {
v, err := value()
if err != nil {
return v, err
}
val = v
updated = o.clock.Now()
}
return val, nil
}, nil
}
var _ StringGetter = (*cachedPlugin)(nil)
func (o *cachedPlugin) StringGetter() (func() (string, error), error) {
return o.cachedGetter(o.value.StringGetter)
}
var _ FloatGetter = (*cachedPlugin)(nil)
func (o *cachedPlugin) FloatGetter() (func() (float64, error), error) {
return o.cachedGetter(o.value.FloatGetter)
}
var _ IntGetter = (*cachedPlugin)(nil)
func (o *cachedPlugin) IntGetter() (func() (int64, error), error) {
return o.cachedGetter(o.value.IntGetter)
}
var _ BoolGetter = (*cachedPlugin)(nil)
func (o *cachedPlugin) BoolGetter() (func() (bool, error), error) {
return o.cachedGetter(o.value.BoolGetter)
}