From 98e6296eaa7f1eaa5b9bced058a1de4a6467cfbe Mon Sep 17 00:00:00 2001 From: andig Date: Tue, 10 Jun 2025 15:11:01 +0200 Subject: [PATCH] Plugins: add valid plugin (#21759) --- plugin/valid.go | 90 +++++++++++++++++++ .../definition/vehicle/ioBroker.bmw.yaml | 60 +++++++++---- 2 files changed, 135 insertions(+), 15 deletions(-) create mode 100644 plugin/valid.go diff --git a/plugin/valid.go b/plugin/valid.go new file mode 100644 index 000000000..1bdec94c7 --- /dev/null +++ b/plugin/valid.go @@ -0,0 +1,90 @@ +package plugin + +import ( + "context" + "fmt" + + "github.com/evcc-io/evcc/api" + "github.com/evcc-io/evcc/util" +) + +func init() { + registry.AddCtx("valid", NewValidFromConfig) +} + +// validPlugin validates a reading via a second reading +type validPlugin struct { + ctx context.Context + valid func() (bool, error) + value Config +} + +// NewValidFromConfig creates valid provider +func NewValidFromConfig(ctx context.Context, other map[string]interface{}) (Plugin, error) { + var cc struct { + Valid, Value Config + } + + if err := util.DecodeOther(other, &cc); err != nil { + return nil, err + } + + valid, err := cc.Valid.BoolGetter(ctx) + if err != nil { + return nil, fmt.Errorf("valid: %w", err) + } + + o := NewValidPlugin(ctx, valid, cc.Value) + + return o, nil +} + +// NewValidPlugin creates valid provider +func NewValidPlugin(ctx context.Context, valid func() (bool, error), value Config) *validPlugin { + return &validPlugin{ + ctx: ctx, + valid: valid, + value: value, + } +} + +var _ StringGetter = (*validPlugin)(nil) + +func validGetter[T any](o *validPlugin, 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("valid: %w", err) + } + + return func() (T, error) { + var zero T + + valid, err := o.valid() + if err != nil { + return zero, err + } + if !valid { + return zero, api.ErrNotAvailable + } + + return value() + }, nil +} + +var _ StringGetter = (*validPlugin)(nil) + +func (o *validPlugin) StringGetter() (func() (string, error), error) { + return validGetter(o, o.value.StringGetter) +} + +var _ FloatGetter = (*validPlugin)(nil) + +func (o *validPlugin) FloatGetter() (func() (float64, error), error) { + return validGetter(o, o.value.FloatGetter) +} + +var _ IntGetter = (*validPlugin)(nil) + +func (o *validPlugin) IntGetter() (func() (int64, error), error) { + return validGetter(o, o.value.IntGetter) +} diff --git a/templates/definition/vehicle/ioBroker.bmw.yaml b/templates/definition/vehicle/ioBroker.bmw.yaml index 652e43ee4..9c79b0829 100644 --- a/templates/definition/vehicle/ioBroker.bmw.yaml +++ b/templates/definition/vehicle/ioBroker.bmw.yaml @@ -32,24 +32,54 @@ render: | type: custom {{ include "vehicle-common" . }} soc: - source: http - uri: {{ .uri }}/getPlainValue/bmw.{{ .id }}.{{ .vin }}.state.electricChargingState.chargingLevelPercent + source: valid + valid: + source: http + uri: {{ .uri }}/getPlainValue/bmw.{{ .id }}.info.connection + cache: 10s + value: + source: http + uri: {{ .uri }}/getPlainValue/bmw.{{ .id }}.{{ .vin }}.state.electricChargingState.chargingLevelPercent limitsoc: - source: http - uri: {{ .uri }}/getPlainValue/bmw.{{ .id }}.{{ .vin }}.state.electricChargingState.chargingTarget + source: valid + valid: + source: http + uri: {{ .uri }}/getPlainValue/bmw.{{ .id }}.info.connection + cache: 10s + value: + source: http + uri: {{ .uri }}/getPlainValue/bmw.{{ .id }}.{{ .vin }}.state.electricChargingState.chargingTarget status: - source: combined - plugged: + source: valid + valid: source: http - uri: {{ .uri }}/getPlainValue/bmw.{{ .id }}.{{ .vin }}.state.electricChargingState.isChargerConnected - charging: - source: http - uri: {{ .uri }}/getPlainValue/bmw.{{ .id }}.{{ .vin }}.state.electricChargingState.chargingStatus - jq: '. == "CHARGING"' + uri: {{ .uri }}/getPlainValue/bmw.{{ .id }}.info.connection + cache: 10s + value: + source: combined + plugged: + source: http + uri: {{ .uri }}/getPlainValue/bmw.{{ .id }}.{{ .vin }}.state.electricChargingState.isChargerConnected + charging: + source: http + uri: {{ .uri }}/getPlainValue/bmw.{{ .id }}.{{ .vin }}.state.electricChargingState.chargingStatus + jq: '. == "CHARGING"' range: - source: http - uri: {{ .uri }}/getPlainValue/bmw.{{ .id }}.{{ .vin }}.state.electricChargingState.range + source: valid + valid: + source: http + uri: {{ .uri }}/getPlainValue/bmw.{{ .id }}.info.connection + cache: 10s + value: + source: http + uri: {{ .uri }}/getPlainValue/bmw.{{ .id }}.{{ .vin }}.state.electricChargingState.range odometer: - source: http - uri: {{ .uri }}/getPlainValue/bmw.{{ .id }}.{{ .vin }}.state.currentMileage + source: valid + valid: + source: http + uri: {{ .uri }}/getPlainValue/bmw.{{ .id }}.info.connection + cache: 10s + value: + source: http + uri: {{ .uri }}/getPlainValue/bmw.{{ .id }}.{{ .vin }}.state.currentMileage {{ include "vehicle-features" . }}