Add MeterReturnEnergy (#29805)
This commit is contained in:
parent
869ac1d6eb
commit
7b5df178ce
20 changed files with 196 additions and 57 deletions
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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))
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue