Ecoflow: add battery params (#28903)

This commit is contained in:
andig 2026-04-07 13:32:38 +02:00 • committed by GitHub
parent 690873570f
commit ed3f44fe19
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 60 additions and 58 deletions

View file

@ -21,7 +21,7 @@ type EcoFlow struct {
client *ecoflow.Client
dataG func() (*ecoflow.GetCmdResponse, error)
gridPower, pvPower, batteryPower, batterySoc string
power, batterySoc string
}
func init() {
@ -31,10 +31,13 @@ func init() {
// NewEcoFlowFromConfig creates an EcoFlow meter from generic config
func NewEcoFlowFromConfig(ctx context.Context, other map[string]any) (api.Meter, error) {
cc := struct {
Usage string
AccessKey, SecretKey, Serial, Region string
GridPower, PvPower, BatteryPower, BatterySoc string
Cache time.Duration
batteryCapacity `mapstructure:",squash"`
batteryPowerLimits `mapstructure:",squash"`
batterySocLimits `mapstructure:",squash"`
Usage string
AccessKey, SecretKey, Serial, Region string
Power, Soc string
Cache time.Duration
}{
Cache: 30 * time.Second,
}
@ -67,14 +70,16 @@ func NewEcoFlowFromConfig(ctx context.Context, other map[string]any) (api.Meter,
return nil, fmt.Errorf("invalid region: %s", cc.Region)
}
m, err := NewEcoFlow(ctx, cc.AccessKey, cc.SecretKey, cc.Serial, cc.Usage, uri,
cc.GridPower, cc.PvPower, cc.BatteryPower, cc.BatterySoc, cc.Cache)
m, err := NewEcoFlow(ctx, cc.AccessKey, cc.SecretKey, cc.Serial, cc.Usage, uri, cc.Power, cc.Soc, cc.Cache)
if err != nil {
return nil, err
}
if cc.Usage == "battery" {
return &EcoFlowBattery{m}, nil
return decorateMeterBattery(
m, nil, m.soc, cc.batteryCapacity.Decorator(),
cc.batterySocLimits.Decorator(), cc.batteryPowerLimits.Decorator(), nil,
), nil
}
return m, nil
@ -82,17 +87,15 @@ func NewEcoFlowFromConfig(ctx context.Context, other map[string]any) (api.Meter,
// NewEcoFlow constructs the EcoFlow struct
func NewEcoFlow(ctx context.Context, accessKey, secretKey, serial, usage, uri string,
gridPower, pvPower, batteryPower, batterySoc string, cache time.Duration) (*EcoFlow, error) {
power, soc string, cache time.Duration) (*EcoFlow, error) {
m := &EcoFlow{
ctx: ctx,
serial: serial,
usage: usage,
cache: cache,
client: ecoflow.NewEcoflowClient(accessKey, secretKey, ecoflow.WithBaseUrl(uri)),
gridPower: gridPower,
pvPower: pvPower,
batteryPower: batteryPower,
batterySoc: batterySoc,
ctx: ctx,
serial: serial,
usage: usage,
cache: cache,
client: ecoflow.NewEcoflowClient(accessKey, secretKey, ecoflow.WithBaseUrl(uri)),
power: power,
batterySoc: soc,
}
m.dataG = util.Cached(m.getData, cache)
@ -105,14 +108,10 @@ func (m *EcoFlow) getData() (*ecoflow.GetCmdResponse, error) {
ctx, cancel := context.WithTimeout(m.ctx, 30*time.Second)
defer cancel()
var params []string
switch m.usage {
case "grid":
params = []string{m.gridPower}
case "pv":
params = []string{m.pvPower}
case "battery":
params = []string{m.batteryPower, m.batterySoc}
params := []string{m.power}
if m.usage == "battery" {
params = append(params, m.batterySoc)
}
return m.client.GetDeviceParameters(ctx, m.serial, params)
@ -127,24 +126,16 @@ func (m *EcoFlow) CurrentPower() (float64, error) {
return 0, err
}
// sysGridPwr responds with int
// mpptPwr responds with an array of floats
// bpPwr responds with int
// bpSoc responds with int
switch m.usage {
case "grid":
return ecoflowValue(response.Data, m.gridPower)
case "pv":
return ecoflowValue(response.Data, m.pvPower)
case "battery":
pwr, err := ecoflowValue(response.Data, m.batteryPower)
if err != nil {
return 0, err
}
return -pwr, nil // invert battery power: ecoflow returns negative when discharging and positive when charging.
default:
return 0, fmt.Errorf("invalid usage: %s", m.usage)
pwr, err := ecoflowValue(response.Data, m.power)
if err != nil {
return 0, err
}
if m.usage == "battery" {
pwr = -pwr // invert battery power: ecoflow returns negative when discharging and positive when charging.
}
return pwr, nil
}
// extractFloat extracts a float64 or int value from a map by key.
@ -157,13 +148,8 @@ func ecoflowValue(data map[string]any, key string) (float64, error) {
return 0, api.ErrNotAvailable
}
// EcoFlowBattery represents the EcoFlow battery decorator
type EcoFlowBattery struct {
*EcoFlow
}
// Soc implements the api.Battery interface for battery usage
func (m *EcoFlowBattery) Soc() (float64, error) {
// soc returns the battery state of charge
func (m *EcoFlow) soc() (float64, error) {
response, err := m.dataG()
if err != nil {
return 0, err

View file

@ -52,6 +52,7 @@ params:
- name: cache
default: 30s
advanced: true
- preset: battery-params
render: |
type: ecoflow
accesskey: {{ .accesskey }}
@ -60,7 +61,14 @@ render: |
usage: {{ .usage }}
region: {{ .region }}
cache: {{ .cache }}
gridpower: sysGridPwr
pvpower: mpptPwr
batterypower: bpPwr
batterysoc: bpSoc
{{- if eq .usage "grid" }}
power: sysGridPwr
{{- end }}
{{- if eq .usage "pv" }}
power: mpptPwr
{{- end }}
{{- if eq .usage "battery" }}
power: bpPwr
soc: bpSoc
{{- include "battery-params" . }}
{{- end }}

View file

@ -53,6 +53,7 @@ params:
- name: cache
default: 30s
advanced: true
- preset: battery-params
render: |
type: ecoflow
accesskey: {{ .accesskey }}
@ -61,7 +62,14 @@ render: |
usage: {{ .usage }}
cache: {{ .cache }}
region: {{ .region }}
gridpower: powGetSysGrid
pvpower: powGetPvSum
batterypower: powGetBpCms
batterysoc: cmsBattSoc
{{- if eq .usage "grid" }}
power: powGetSysGrid
{{- end }}
{{- if eq .usage "pv" }}
power: powGetPvSum
{{- end }}
{{- if eq .usage "battery" }}
power: powGetBpCms
soc: cmsBattSoc
{{- include "battery-params" . }}
{{- end }}