Add MeterReturnEnergy (#29805)

This commit is contained in:
andig 2026-06-02 13:53:13 +02:00 • committed by GitHub
parent 869ac1d6eb
commit 7b5df178ce
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
20 changed files with 196 additions and 57 deletions

View file

@ -8,24 +8,31 @@ import (
)
type Energy struct {
Power plugin.Config
Energy *plugin.Config // optional
Power plugin.Config
Energy *plugin.Config // optional
ReturnEnergy *plugin.Config // optional
}
func (cc *Energy) Configure(ctx context.Context) (
func() (float64, error),
func() (float64, error),
func() (float64, error),
error,
) {
powerG, err := cc.Power.FloatGetter(ctx)
if err != nil {
return nil, nil, fmt.Errorf("power: %w", err)
return nil, nil, nil, fmt.Errorf("power: %w", err)
}
energyG, err := cc.Energy.FloatGetter(ctx)
if err != nil {
return nil, nil, fmt.Errorf("energy: %w", err)
return nil, nil, nil, fmt.Errorf("energy: %w", err)
}
return powerG, energyG, nil
returnG, err := cc.ReturnEnergy.FloatGetter(ctx)
if err != nil {
return nil, nil, nil, fmt.Errorf("returnEnergy: %w", err)
}
return powerG, energyG, returnG, nil
}

View file

@ -44,13 +44,14 @@ func NewConfigurableFromConfig(ctx context.Context, other map[string]any) (api.M
return nil, err
}
powerG, energyG, err := cc.Energy.Configure(ctx)
powerG, energyG, returnG, err := cc.Energy.Configure(ctx)
if err != nil {
return nil, err
}
m, _ := NewConfigurable(powerG)
implement.May(m, implement.MeterEnergy(energyG))
implement.May(m, implement.MeterReturnEnergy(returnG))
// dim/curtail
if err := cc.Dimmer.Implement(ctx, m); err != nil {

View file

@ -47,6 +47,12 @@ func NewMovingAverageFromConfig(ctx context.Context, other map[string]any) (api.
totalEnergy = m.TotalEnergy
}
// decorate return energy reading
var returnEnergy func() (float64, error)
if m, ok := api.Cap[api.MeterReturnEnergy](m); ok {
returnEnergy = m.ReturnEnergy
}
// decorate battery reading
var batterySoc func() (float64, error)
if m, ok := api.Cap[api.Battery](m); ok {
@ -72,6 +78,7 @@ func NewMovingAverageFromConfig(ctx context.Context, other map[string]any) (api.
}
implement.May(meter, implement.MeterEnergy(totalEnergy))
implement.May(meter, implement.MeterReturnEnergy(returnEnergy))
if batterySoc != nil {
implement.Has(meter, implement.Battery(batterySoc))

View file

@ -6,22 +6,24 @@ import (
"strings"
"time"
"github.com/evcc-io/evcc/api"
"github.com/evcc-io/evcc/util"
"github.com/evcc-io/evcc/util/request"
"github.com/evcc-io/evcc/util/transport"
)
type Generation interface {
CurrentPower() (float64, error)
Enabled() (bool, error)
Enable(bool) error
TotalEnergy() (float64, error)
api.Meter
api.MeterEnergy
api.MeterReturnEnergy
}
type Phases interface {
Currents() (float64, float64, float64, error)
Voltages() (float64, float64, float64, error)
Powers() (float64, float64, float64, error)
api.PhaseCurrents
api.PhaseVoltages
api.PhasePowers
}
// Connection is the Shelly connection

View file

@ -125,6 +125,25 @@ func (c *gen1) TotalEnergy() (float64, error) {
return c.energy(energy) / 1000, nil
}
func (c *gen1) ReturnEnergy() (float64, error) {
var energy float64
res, err := c.status.Get()
if err != nil {
return 0, err
}
switch {
case c.channel < len(res.Meters):
energy = res.Meters[c.channel].Total_Returned
case c.channel < len(res.EMeters):
energy = res.EMeters[c.channel].Total_Returned
default:
return 0, errors.New("invalid channel, missing power meter")
}
return c.energy(energy) / 1000, nil
}
// gen1Energy in kWh
func (c *gen1) energy(energy float64) float64 {
// Gen 1 Shelly EM devices are providing Watt hours, Gen 1 Shelly PM devices are providing Watt minutes

View file

@ -219,7 +219,7 @@ func (c *gen2) Enable(enable bool) error {
return c.execEnableCmd(c.switchchannel, "Switch.Set", enable, &res)
}
// TotalEnergy implements the api.Meter interface
// TotalEnergy implements the api.MeterEnergy interface
func (c *gen2) TotalEnergy() (float64, error) {
switch {
case c.hasEM1Endpoint():
@ -239,6 +239,26 @@ func (c *gen2) TotalEnergy() (float64, error) {
}
}
// ReturnEnergy implements the api.MeterReturnEnergy interface
func (c *gen2) ReturnEnergy() (float64, error) {
switch {
case c.hasEM1Endpoint():
res, err := c.em1data()
return res.TotalActRetEnergy / 1000, err
case c.hasEMEndpoint():
res, err := c.emdata()
return res.TotalActRet / 1000, err
case c.hasSwitchEndpoint():
res, err := c.switchstatus.Get()
return res.Ret_Aenergy.Total / 1000, err
default:
return 0, fmt.Errorf("unknown shelly model: %s", c.model)
}
}
// Currents implements the api.PhaseCurrents interface
func (c *gen2) Currents() (float64, float64, float64, error) {
switch {