evcc-io/meter/homeassistant.go

109 lines
3.3 KiB
Go

package meter
//go:generate go tool decorate -f decorateHomeAssistant -b *HomeAssistant -r api.Meter -t "api.MeterEnergy,TotalEnergy,func() (float64, error)" -t "api.PhaseCurrents,Currents,func() (float64, float64, float64, error)" -t "api.PhaseVoltages,Voltages,func() (float64, float64, float64, error)" -t "api.Battery,Soc,func() (float64, error)" -t "api.BatteryCapacity,Capacity,func() float64" -t "api.BatterySocLimiter,GetSocLimits,func() (float64, float64)" -t "api.BatteryPowerLimiter,GetPowerLimits,func() (float64, float64)" -t "api.BatteryController,SetBatteryMode,func(api.BatteryMode) error" -t "api.MaxACPowerGetter,MaxACPower,func() float64"
import (
"errors"
"fmt"
"github.com/evcc-io/evcc/api"
"github.com/evcc-io/evcc/util"
"github.com/evcc-io/evcc/util/homeassistant"
)
// HomeAssistant meter implementation
type HomeAssistant struct {
conn *homeassistant.Connection
power string
}
func init() {
registry.Add("homeassistant", NewHomeAssistantFromConfig)
}
// NewHomeAssistantFromConfig creates a HomeAssistant meter from generic config
func NewHomeAssistantFromConfig(other map[string]any) (api.Meter, error) {
cc := struct {
_URI string `mapstructure:"uri"` // TODO deprecated
_Token string `mapstructure:"token"` // TODO deprecated
Home string
Power string
Energy string
Currents []string
Voltages []string
Soc string
// pv
pvMaxACPower `mapstructure:",squash"`
// battery
batteryCapacity `mapstructure:",squash"`
batterySocLimits `mapstructure:",squash"`
batteryPowerLimits `mapstructure:",squash"`
}{
Home: "Home",
batterySocLimits: batterySocLimits{
MinSoc: 20,
MaxSoc: 95,
},
}
if err := util.DecodeOther(other, &cc); err != nil {
return nil, err
}
if cc.Power == "" {
return nil, errors.New("missing power sensor entity")
}
log := util.NewLogger("ha-meter")
conn, err := homeassistant.NewConnection(log, cc.Home)
if err != nil {
return nil, err
}
m := &HomeAssistant{
conn: conn,
power: cc.Power,
}
// decorators for optional interfaces
var energy func() (float64, error)
var currents, voltages func() (float64, float64, float64, error)
var soc func() (float64, error)
if cc.Energy != "" {
energy = func() (float64, error) { return conn.GetFloatState(cc.Energy) }
}
// phase currents (optional)
if phases, err := homeassistant.ValidatePhaseEntities(cc.Currents); len(phases) > 0 {
currents = func() (float64, float64, float64, error) { return conn.GetPhaseFloatStates(phases) }
} else if err != nil {
return nil, fmt.Errorf("currents: %w", err)
}
// phase voltages (optional)
if phases, err := homeassistant.ValidatePhaseEntities(cc.Voltages); len(phases) > 0 {
voltages = func() (float64, float64, float64, error) { return conn.GetPhaseFloatStates(phases) }
} else if err != nil {
return nil, fmt.Errorf("voltages: %w", err)
}
if cc.Soc != "" {
soc = func() (float64, error) { return conn.GetFloatState(cc.Soc) }
}
return decorateHomeAssistant(m,
energy, currents, voltages, soc,
cc.batteryCapacity.Decorator(), cc.batterySocLimits.Decorator(), cc.batteryPowerLimits.Decorator(), nil,
cc.pvMaxACPower.Decorator(),
), nil
}
var _ api.Meter = (*HomeAssistant)(nil)
// CurrentPower implements the api.Meter interface
func (m *HomeAssistant) CurrentPower() (float64, error) {
return m.conn.GetFloatState(m.power)
}