Teslamate: add charged energy (#28611)

This commit is contained in:
Hofy 2026-03-28 13:59:46 +01:00 • committed by GitHub
parent f4932f9fbe
commit bd9769c152
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 34 additions and 4 deletions

View file

@ -51,6 +51,10 @@ render: |
topic: teslamate/cars/{{ .id }}/time_to_full_charge
timeout: {{ .timeout }}
jq: if (. == null) or (. == 0) or (type != "number") or (. < 0) then null else (now + (. * 3600) | strftime("%Y-%m-%dT%H:%M:%SZ")) end
chargedenergy:
source: mqtt
topic: teslamate/cars/{{ .id }}/charge_energy_added
timeout: {{ .timeout }}
climater:
source: mqtt
topic: teslamate/cars/{{ .id }}/is_preconditioning

View file

@ -128,6 +128,7 @@ func NewHomeAssistantVehicleFromConfig(other map[string]any) (api.Vehicle, error
finish,
wakeup,
enable,
nil,
), nil
}

View file

@ -11,7 +11,7 @@ import (
"github.com/evcc-io/evcc/util"
)
//go:generate go tool decorate -f decorateVehicle -b api.Vehicle -t api.SocLimiter,api.ChargeState,api.VehicleRange,api.VehicleOdometer,api.VehicleClimater,api.CurrentController,api.CurrentGetter,api.VehicleFinishTimer,api.Resurrector,api.ChargeController
//go:generate go tool decorate -f decorateVehicle -b api.Vehicle -t api.SocLimiter,api.ChargeState,api.VehicleRange,api.VehicleOdometer,api.VehicleClimater,api.CurrentController,api.CurrentGetter,api.VehicleFinishTimer,api.Resurrector,api.ChargeController,api.ChargeRater
// Vehicle is an api.Vehicle implementation with configurable getters and setters.
type Vehicle struct {
@ -38,6 +38,7 @@ func NewConfigurableFromConfig(ctx context.Context, other map[string]any) (api.V
FinishTime *plugin.Config
Wakeup *plugin.Config
ChargeEnable *plugin.Config
ChargedEnergy *plugin.Config
}
if err := util.DecodeOther(other, &cc); err != nil {
@ -140,6 +141,16 @@ func NewConfigurableFromConfig(ctx context.Context, other map[string]any) (api.V
return nil, fmt.Errorf("chargeEnable: %w", err)
}
// decorate chargedenergy
var chargedEnergy func() (float64, error)
if cc.ChargedEnergy != nil {
var err error
chargedEnergy, err = cc.ChargedEnergy.FloatGetter(ctx)
if err != nil {
return nil, fmt.Errorf("charged energy: %w", err)
}
}
switch {
case maxCurrent == nil && getMaxCurrent != nil:
return nil, errors.New("cannot have current without current control")
@ -149,7 +160,7 @@ func NewConfigurableFromConfig(ctx context.Context, other map[string]any) (api.V
return nil, errors.New("cannot have charge control without status")
}
return decorateVehicle(v, limitSoc, status, rng, odo, climater, maxCurrent, getMaxCurrent, finishTime, wakeup, chargeEnable), nil
return decorateVehicle(v, limitSoc, status, rng, odo, climater, maxCurrent, getMaxCurrent, finishTime, wakeup, chargeEnable, chargedEnergy), nil
}
// Soc implements the api.Vehicle interface

View file

@ -9,7 +9,7 @@ import (
"github.com/evcc-io/evcc/api"
)
func decorateVehicle(base api.Vehicle, socLimiter func() (int64, error), chargeState func() (api.ChargeStatus, error), vehicleRange func() (int64, error), vehicleOdometer func() (float64, error), vehicleClimater func() (bool, error), currentController func(int64) error, currentGetter func() (float64, error), vehicleFinishTimer func() (time.Time, error), resurrector func() error, chargeController func(bool) error) api.Vehicle {
func decorateVehicle(base api.Vehicle, socLimiter func() (int64, error), chargeState func() (api.ChargeStatus, error), vehicleRange func() (int64, error), vehicleOdometer func() (float64, error), vehicleClimater func() (bool, error), currentController func(int64) error, currentGetter func() (float64, error), vehicleFinishTimer func() (time.Time, error), resurrector func() error, chargeController func(bool) error, chargeRater func() (float64, error)) api.Vehicle {
caps := make(map[reflect.Type]any)
if socLimiter != nil {
@ -52,6 +52,10 @@ func decorateVehicle(base api.Vehicle, socLimiter func() (int64, error), chargeS
caps[reflect.TypeFor[api.ChargeController]()] = &decorateVehicleChargeControllerImpl{chargeController: chargeController}
}
if chargeRater != nil {
caps[reflect.TypeFor[api.ChargeRater]()] = &decorateVehicleChargeRaterImpl{chargeRater: chargeRater}
}
if len(caps) == 0 {
return base
}
@ -77,6 +81,14 @@ func (impl *decorateVehicleChargeControllerImpl) ChargeEnable(p0 bool) error {
return impl.chargeController(p0)
}
type decorateVehicleChargeRaterImpl struct {
chargeRater func() (float64, error)
}
func (impl *decorateVehicleChargeRaterImpl) ChargedEnergy() (float64, error) {
return impl.chargeRater()
}
type decorateVehicleChargeStateImpl struct {
chargeState func() (api.ChargeStatus, error)
}

View file

@ -17,7 +17,7 @@ func baseVehicle() *Vehicle {
}
func TestDecorateVehicle_NoCapabilities(t *testing.T) {
v := decorateVehicle(baseVehicle(), nil, nil, nil, nil, nil, nil, nil, nil, nil, nil)
v := decorateVehicle(baseVehicle(), nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil)
// base vehicle interface works
soc, err := v.Soc()
@ -42,6 +42,7 @@ func TestDecorateVehicle_AllCapabilities(t *testing.T) {
func() (time.Time, error) { return time.Date(2026, 1, 1, 0, 0, 0, 0, time.UTC), nil }, // vehicleFinishTimer
func() error { return nil }, // resurrector
func(bool) error { return nil }, // chargeController
nil, // chargeRater
)
// base interface
@ -126,6 +127,7 @@ func TestDecorateVehicle_PartialCapabilities(t *testing.T) {
nil, // vehicleFinishTimer
func() error { return nil }, // resurrector
nil, // chargeController
nil, // chargeRater
)
assert.True(t, api.HasCap[api.SocLimiter](v), "SocLimiter should be present")