Revert "api: split MeterEnergy into MeterImport and MeterExport (#29788)"

This reverts commit 7a5b1e7560.
This commit is contained in:
andig 2026-05-10 09:07:13 +02:00
parent 7d4da17e4a
commit d5e6253d35
128 changed files with 435 additions and 570 deletions

View file

@ -49,10 +49,10 @@ func (m *Blueprint) CurrentPower() (float64, error) {
return 0, api.ErrNotAvailable
}
var _ api.MeterImport = (*Blueprint)(nil)
var _ api.MeterEnergy = (*Blueprint)(nil)
// ImportEnergy implements the api.MeterImport interface
func (m *Blueprint) ImportEnergy() (float64, error) {
// TotalEnergy implements the api.MeterEnergy interface
func (m *Blueprint) TotalEnergy() (float64, error) {
return 0, api.ErrNotAvailable
}

View file

@ -67,10 +67,10 @@ func (wb *CfosPowerBrain) CurrentPower() (float64, error) {
return float64(binary.BigEndian.Uint32(b)), err
}
var _ api.MeterImport = (*CfosPowerBrain)(nil)
var _ api.MeterEnergy = (*CfosPowerBrain)(nil)
// ImportEnergy implements the api.MeterImport interface
func (wb *CfosPowerBrain) ImportEnergy() (float64, error) {
// TotalEnergy implements the api.MeterEnergy interface
func (wb *CfosPowerBrain) TotalEnergy() (float64, error) {
b, err := wb.conn.ReadHoldingRegisters(cfosRegEnergy, 4)
if err != nil {
return 0, err

View file

@ -103,7 +103,7 @@ func NewDanfossTLX(ctx context.Context, cfg comlynx.Config, maxACPower func() fl
}
if hasEnergy == nil {
implement.Has(m, implement.MeterImport(m.totalEnergy))
implement.Has(m, implement.MeterEnergy(m.totalEnergy))
}
if hasVoltages == nil {
implement.Has(m, implement.PhaseVoltages(m.phaseVoltages))

View file

@ -100,9 +100,9 @@ func (m *Discovergy) CurrentPower() (float64, error) {
return m.scale * float64(res.Values.Power) / 1e3, err
}
var _ api.MeterImport = (*Discovergy)(nil)
var _ api.MeterEnergy = (*Discovergy)(nil)
func (m *Discovergy) ImportEnergy() (float64, error) {
func (m *Discovergy) TotalEnergy() (float64, error) {
res, err := m.dataG()
return float64(res.Values.Energy) / 1e10, err
}

View file

@ -116,7 +116,7 @@ func NewDsmr(uri, energy string, timeout time.Duration) (api.Meter, error) {
// decorate energy reading
if energy != "" {
implement.Has(m, implement.MeterImport(m.totalEnergy))
implement.Has(m, implement.MeterEnergy(m.totalEnergy))
}
// decorate currents
@ -284,7 +284,7 @@ func (m *Dsmr) CurrentPower() (float64, error) {
return (bezug - lief) * 1e3, errors.Join(err1, err2)
}
// totalEnergy implements the api.MeterImport interface
// totalEnergy implements the api.MeterEnergy interface
func (m *Dsmr) totalEnergy() (float64, error) {
return m.get(m.energy)
}

View file

@ -176,9 +176,9 @@ func (c *EEBus) CurrentPower() (float64, error) {
return c.readValue(c.scenarios.power, c.mm.Power)
}
var _ api.MeterImport = (*EEBus)(nil)
var _ api.MeterEnergy = (*EEBus)(nil)
func (c *EEBus) ImportEnergy() (float64, error) {
func (c *EEBus) TotalEnergy() (float64, error) {
return c.readValue(c.scenarios.energy, c.mm.EnergyConsumed)
}

View file

@ -91,10 +91,10 @@ func (c *Connection) CurrentPower() (float64, error) {
return power / 1000, err // mW ==> W
}
var _ api.MeterImport = (*Connection)(nil)
var _ api.MeterEnergy = (*Connection)(nil)
// ImportEnergy implements the api.MeterImport interface
func (c *Connection) ImportEnergy() (float64, error) {
// TotalEnergy implements the api.MeterEnergy interface
func (c *Connection) TotalEnergy() (float64, error) {
// Energy value in Wh (total switch energy, refresh approximately every 2 minutes)
resp, err := c.ExecCmd("getswitchenergy")
if err != nil {

View file

@ -3,7 +3,7 @@ package fritz
// Meter defines the interface for Fritz connections (both legacy LUA and REST)
type Meter interface {
CurrentPower() (float64, error)
ImportEnergy() (float64, error)
TotalEnergy() (float64, error)
}
// Switch extends Meter with switch control capabilities

View file

@ -138,10 +138,10 @@ func (c *Connection) CurrentPower() (float64, error) {
return 0, api.ErrNotAvailable
}
var _ api.MeterImport = (*Connection)(nil)
var _ api.MeterEnergy = (*Connection)(nil)
// ImportEnergy implements the api.MeterImport interface
func (c *Connection) ImportEnergy() (float64, error) {
// TotalEnergy implements the api.MeterEnergy interface
func (c *Connection) TotalEnergy() (float64, error) {
unit, err := c.unitG.Get()
if err != nil {
return 0, err

View file

@ -89,7 +89,7 @@ func NewHomeAssistantFromConfig(other map[string]any) (api.Meter, error) {
powersG = func() (float64, float64, float64, error) { return conn.GetPhaseFloatStates(phases) }
}
implement.May(m, implement.MeterImport(energyG))
implement.May(m, implement.MeterEnergy(energyG))
if cc.Soc != "" {
socG := func() (float64, error) { return conn.GetFloatState(cc.Soc) }

View file

@ -63,12 +63,12 @@ func (c *CCU) CurrentPower() (float64, error) {
return c.conn.CurrentPower()
}
var _ api.MeterImport = (*CCU)(nil)
var _ api.MeterEnergy = (*CCU)(nil)
// ImportEnergy implements the api.MeterImport interface
func (c *CCU) ImportEnergy() (float64, error) {
// TotalEnergy implements the api.MeterEnergy interface
func (c *CCU) TotalEnergy() (float64, error) {
if c.usage == "grid" {
return c.conn.GridImportEnergy()
return c.conn.GridTotalEnergy()
}
return c.conn.ImportEnergy()
return c.conn.TotalEnergy()
}

View file

@ -91,8 +91,8 @@ func (c *Connection) CurrentPower() (float64, error) {
return res.FloatValue("POWER"), err
}
// ImportEnergy reads the homematic HMIP-PSM meterchannel energy in Wh
func (c *Connection) ImportEnergy() (float64, error) {
// TotalEnergy reads the homematic HMIP-PSM meterchannel energy in Wh
func (c *Connection) TotalEnergy() (float64, error) {
res, err := c.meterG.Get()
return res.FloatValue("ENERGY_COUNTER") / 1e3, err
}
@ -109,8 +109,8 @@ func (c *Connection) GridCurrentPower() (float64, error) {
return res.FloatValue("IEC_POWER"), err
}
// GridImportEnergy reads the homematic HM-ES-TX-WM grid meterchannel energy in kWh
func (c *Connection) GridImportEnergy() (float64, error) {
// GridTotalEnergy reads the homematic HM-ES-TX-WM grid meterchannel energy in kWh
func (c *Connection) GridTotalEnergy() (float64, error) {
res, err := c.meterG.Get()
return res.FloatValue("IEC_ENERGY_COUNTER"), err
}

View file

@ -56,11 +56,11 @@ func (c *HomeWizard) CurrentPower() (float64, error) {
return c.conn.CurrentPower()
}
var _ api.MeterImport = (*HomeWizard)(nil)
var _ api.MeterEnergy = (*HomeWizard)(nil)
// ImportEnergy implements the api.MeterImport interface
func (c *HomeWizard) ImportEnergy() (float64, error) {
return c.conn.ImportEnergy()
// TotalEnergy implements the api.MeterEnergy interface
func (c *HomeWizard) TotalEnergy() (float64, error) {
return c.conn.TotalEnergy()
}
var _ api.PhaseCurrents = (*HomeWizard)(nil)

View file

@ -107,8 +107,8 @@ func (c *Connection) CurrentPower() (float64, error) {
return res.ActivePowerW, err
}
// ImportEnergy implements the api.MeterImport interface
func (c *Connection) ImportEnergy() (float64, error) {
// TotalEnergy implements the api.MeterEnergy interface
func (c *Connection) TotalEnergy() (float64, error) {
res, err := c.dataG.Get()
if c.usage == "pv" {
return res.TotalPowerExportT1kWh + res.TotalPowerExportT2kWh + res.TotalPowerExportT3kWh + res.TotalPowerExportT4kWh, err

View file

@ -77,7 +77,7 @@ func NewLgEss(uri, usage, registration, password string, cache time.Duration, ba
implement.May(m, implement.BatteryPowerLimiter(batteryPowerLimits.Decorator()))
if m.usage == "grid" && essType != lgpcs.LgEss15 {
implement.Has(m, implement.MeterImport(m.totalEnergy))
implement.Has(m, implement.MeterEnergy(m.totalEnergy))
}
if usage == "battery" {
@ -111,7 +111,7 @@ func (m *LgEss) CurrentPower() (float64, error) {
}
}
// totalEnergy implements the api.MeterImport interface
// totalEnergy implements the api.MeterEnergy interface
func (m *LgEss) totalEnergy() (float64, error) {
data, err := m.conn.Data()
if err != nil {

View file

@ -100,7 +100,7 @@ func NewMbmdFromConfig(ctx context.Context, other map[string]any) (api.Meter, er
if err != nil {
return nil, fmt.Errorf("invalid measurement for energy: %s", cc.Energy)
}
implement.Has(m, implement.MeterImport(totalEnergy))
implement.Has(m, implement.MeterEnergy(totalEnergy))
}
// decorate soc

View file

@ -9,47 +9,23 @@ import (
type Energy struct {
Power plugin.Config
Import *plugin.Config // optional
Export *plugin.Config // optional
// Energy is a legacy alias used when neither Import nor Export is set.
// For meters, the same value is wired to both interfaces and the site role
// (pv/battery → Export; grid/aux/ext → Import) decides which one is read.
Energy *plugin.Config
Energy *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, nil, fmt.Errorf("power: %w", err)
return nil, nil, fmt.Errorf("power: %w", err)
}
// legacy: a single energy field is exposed as both import and export;
// the site role decides which interface is used.
importCfg := cc.Import
exportCfg := cc.Export
if cc.Energy != nil {
if importCfg == nil {
importCfg = cc.Energy
}
if exportCfg == nil {
exportCfg = cc.Energy
}
}
importG, err := importCfg.FloatGetter(ctx)
energyG, err := cc.Energy.FloatGetter(ctx)
if err != nil {
return nil, nil, nil, fmt.Errorf("import: %w", err)
return nil, nil, fmt.Errorf("energy: %w", err)
}
exportG, err := exportCfg.FloatGetter(ctx)
if err != nil {
return nil, nil, nil, fmt.Errorf("export: %w", err)
}
return powerG, importG, exportG, nil
return powerG, energyG, nil
}

View file

@ -42,14 +42,13 @@ func NewConfigurableFromConfig(ctx context.Context, other map[string]any) (api.M
return nil, err
}
powerG, importG, exportG, err := cc.Energy.Configure(ctx)
powerG, energyG, err := cc.Energy.Configure(ctx)
if err != nil {
return nil, err
}
m, _ := NewConfigurable(powerG)
implement.May(m, implement.MeterImport(importG))
implement.May(m, implement.MeterExport(exportG))
implement.May(m, implement.MeterEnergy(energyG))
// decorate soc
socG, err := cc.Soc.FloatGetter(ctx)

View file

@ -41,16 +41,10 @@ func NewMovingAverageFromConfig(ctx context.Context, other map[string]any) (api.
meter, _ := NewConfigurable(mav.CurrentPower)
// decorate import reading
var importEnergy func() (float64, error)
if m, ok := api.Cap[api.MeterImport](m); ok {
importEnergy = m.ImportEnergy
}
// decorate export reading
var exportEnergy func() (float64, error)
if m, ok := api.Cap[api.MeterExport](m); ok {
exportEnergy = m.ExportEnergy
// decorate energy reading
var totalEnergy func() (float64, error)
if m, ok := api.Cap[api.MeterEnergy](m); ok {
totalEnergy = m.TotalEnergy
}
// decorate battery reading
@ -77,8 +71,7 @@ func NewMovingAverageFromConfig(ctx context.Context, other map[string]any) (api.
powers = m.Powers
}
implement.May(meter, implement.MeterImport(importEnergy))
implement.May(meter, implement.MeterExport(exportEnergy))
implement.May(meter, implement.MeterEnergy(totalEnergy))
if batterySoc != nil {
implement.Has(meter, implement.Battery(batterySoc))

View file

@ -142,12 +142,8 @@ func NewPowerWall(uri, usage, user, password string, cache time.Duration, refres
m.energySite = energySite
}
if m.usage == "load" {
implement.Has(m, implement.MeterImport(m.totalEnergy))
}
if m.usage == "solar" {
implement.Has(m, implement.MeterExport(m.totalEnergy))
if m.usage == "load" || m.usage == "solar" {
implement.Has(m, implement.MeterEnergy(m.totalEnergy))
}
if usage == "battery" {
@ -197,7 +193,7 @@ func (m *PowerWall) CurrentPower() (float64, error) {
return 0, fmt.Errorf("invalid usage: %s", m.usage)
}
// totalEnergy returns import (load) or export (solar) energy depending on usage
// totalEnergy implements the api.MeterEnergy interface
func (m *PowerWall) totalEnergy() (float64, error) {
res, err := m.meterG()
if err != nil {

View file

@ -100,7 +100,7 @@ func NewRCT(ctx context.Context, uri, usage string, batterySocLimits batterySocL
}
if usage == "grid" {
implement.Has(m, implement.MeterImport(m.totalEnergy))
implement.Has(m, implement.MeterEnergy(m.totalEnergy))
}
if usage == "pv" {
@ -264,7 +264,7 @@ func (m *RCT) CurrentPower() (float64, error) {
}
}
// totalEnergy implements the api.MeterImport interface
// totalEnergy implements the api.MeterEnergy interface
func (m *RCT) totalEnergy() (float64, error) {
switch m.usage {
case "grid":

View file

@ -15,7 +15,7 @@ type Generation interface {
CurrentPower() (float64, error)
Enabled() (bool, error)
Enable(bool) error
ImportEnergy() (float64, error)
TotalEnergy() (float64, error)
}
type Phases interface {

View file

@ -106,7 +106,7 @@ func (c *gen1) Enable(enable bool) error {
return c.GetJSON(uri, &res)
}
func (c *gen1) ImportEnergy() (float64, error) {
func (c *gen1) TotalEnergy() (float64, error) {
var energy float64
res, err := c.status.Get()
if err != nil {

View file

@ -219,8 +219,8 @@ func (c *gen2) Enable(enable bool) error {
return c.execEnableCmd(c.switchchannel, "Switch.Set", enable, &res)
}
// ImportEnergy implements the api.MeterImport interface
func (c *gen2) ImportEnergy() (float64, error) {
// TotalEnergy implements the api.Meter interface
func (c *gen2) TotalEnergy() (float64, error) {
switch {
case c.hasEM1Endpoint():
res, err := c.em1data()

View file

@ -87,7 +87,7 @@ func NewSMA(uri, password, iface string, serial uint32, scale float64, usage str
go sm.device.Run()
if usage == "battery" {
implement.May(sm, implement.MeterExport(sm.ExportEnergy))
implement.May(sm, implement.MeterEnergy(sm.TotalEnergy))
implement.Has(sm, implement.Battery(sm.soc))
implement.May(sm, implement.BatteryCapacity(capacity))
implement.May(sm, implement.BatterySocLimiter(batterySocLimits))
@ -103,10 +103,10 @@ func (sm *SMA) CurrentPower() (float64, error) {
return sm.scale * (sma.AsFloat(values[sunny.ActivePowerPlus]) - sma.AsFloat(values[sunny.ActivePowerMinus])), err
}
var _ api.MeterExport = (*SMA)(nil)
var _ api.MeterEnergy = (*SMA)(nil)
// ExportEnergy implements the api.MeterExport interface
func (sm *SMA) ExportEnergy() (float64, error) {
// TotalEnergy implements the api.MeterEnergy interface
func (sm *SMA) TotalEnergy() (float64, error) {
values, err := sm.device.Values()
if sm.scale < 0 {
return sma.AsFloat(values[sunny.ActiveEnergyMinus]) / 3600000, err

View file

@ -89,11 +89,11 @@ func (c *Tasmota) CurrentPower() (float64, error) {
return power, nil
}
var _ api.MeterImport = (*Tasmota)(nil)
var _ api.MeterEnergy = (*Tasmota)(nil)
// ImportEnergy implements the api.MeterImport interface
func (c *Tasmota) ImportEnergy() (float64, error) {
return c.conn.ImportEnergy()
// TotalEnergy implements the api.MeterEnergy interface
func (c *Tasmota) TotalEnergy() (float64, error) {
return c.conn.TotalEnergy()
}
// powers implements the api.PhasePowers interface

View file

@ -222,8 +222,8 @@ func (c *Connection) CurrentPower() (float64, error) {
return res, nil
}
// ImportEnergy implements the api.MeterImport interface
func (c *Connection) ImportEnergy() (float64, error) {
// TotalEnergy implements the api.MeterEnergy interface
func (c *Connection) TotalEnergy() (float64, error) {
res, err := c.statusSnsG.Get()
if err != nil {
return 0, err

View file

@ -94,8 +94,8 @@ func (d *Connection) CurrentPower() (float64, error) {
return power, nil
}
// ImportEnergy implements the api.MeterImport interface
func (d *Connection) ImportEnergy() (float64, error) {
// TotalEnergy implements the api.MeterEnergy interface
func (d *Connection) TotalEnergy() (float64, error) {
var res EmeterResponse
if err := d.ExecCmd(`{"emeter":{"get_realtime":null}}`, &res); err != nil {
return 0, err

View file

@ -143,9 +143,9 @@ func (m *TqEm) CurrentPower() (float64, error) {
return res.Obis1_4_0 - res.Obis2_4_0, err
}
var _ api.MeterImport = (*TqEm)(nil)
var _ api.MeterEnergy = (*TqEm)(nil)
func (m *TqEm) ImportEnergy() (float64, error) {
func (m *TqEm) TotalEnergy() (float64, error) {
res, err := m.dataG()
return res.Obis1_8_0 / 1e3, err
}

View file

@ -146,9 +146,9 @@ func (m *TqEM420) CurrentPower() (float64, error) {
return (res.SmartMeter.Values.ActivePowerP - res.SmartMeter.Values.ActivePowerM) / 1e3, err
}
var _ api.MeterImport = (*TqEM420)(nil)
var _ api.MeterEnergy = (*TqEM420)(nil)
func (m *TqEM420) ImportEnergy() (float64, error) {
func (m *TqEM420) TotalEnergy() (float64, error) {
res, err := m.dataG()
return res.SmartMeter.Values.ActiveEnergyP / 1e6, err
}