package core import ( "errors" "fmt" "slices" "github.com/cenkalti/backoff/v4" "github.com/evcc-io/evcc/api" "github.com/evcc-io/evcc/core/keys" "github.com/evcc-io/evcc/util/config" "github.com/evcc-io/evcc/util/modbus" "github.com/samber/lo" ) type circuitStruct struct { Title string `json:"title,omitempty"` Icon string `json:"icon,omitempty"` Parent string `json:"parent,omitempty"` Power float64 `json:"power"` Current *float64 `json:"current,omitempty"` MaxPower float64 `json:"maxPower,omitempty"` MaxCurrent float64 `json:"maxCurrent,omitempty"` } // publishCircuits returns a list of circuit titles func (site *Site) publishCircuits() { cc := config.Circuits().Devices() res := make(map[string]circuitStruct, len(cc)) names := make(map[api.Circuit]string, len(cc)) for _, c := range cc { names[c.Instance()] = c.Config().Name } for _, c := range cc { instance := c.Instance() props := deviceProperties(c) data := circuitStruct{ Title: instance.GetTitle(), Icon: props.Icon, Parent: names[instance.GetParent()], Power: instance.GetChargePower(), MaxPower: instance.GetMaxPower(), MaxCurrent: instance.GetMaxCurrent(), } if instance.GetMaxCurrent() > 0 { data.Current = lo.EmptyableToPtr(instance.GetMaxPhaseCurrent()) } res[c.Config().Name] = data } site.publish(keys.Circuits, res) } func (site *Site) dimMeters(dim *bool) error { if dim == nil { return nil } var errs error for _, dev := range slices.Concat(site.auxMeters, site.extMeters) { m, ok := api.Cap[api.Dimmer](dev.Instance()) if !ok { continue } if dimmed, err := backoff.RetryWithData(m.Dimmed, modbus.Backoff()); err == nil { if *dim == dimmed { continue } } else { if !errors.Is(err, api.ErrNotAvailable) { errs = errors.Join(errs, fmt.Errorf("%s dimmed: %w", deviceTitleOrName(dev), err)) } continue } if err := m.Dim(*dim); err == nil { site.log.DEBUG.Printf("%s dim: %t", deviceTitleOrName(dev), *dim) } else if !errors.Is(err, api.ErrNotAvailable) { errs = errors.Join(errs, fmt.Errorf("%s dim: %w", deviceTitleOrName(dev), err)) } } return errs } func (site *Site) curtailPV(curtail *bool) error { if curtail == nil { return nil } var errs error for _, dev := range site.pvMeters { m, ok := api.Cap[api.Curtailer](dev.Instance()) if !ok { continue } if curtailed, err := backoff.RetryWithData(m.Curtailed, modbus.Backoff()); err == nil { if *curtail == curtailed { continue } } else { if !errors.Is(err, api.ErrNotAvailable) { errs = errors.Join(errs, fmt.Errorf("%s curtailed: %w", deviceTitleOrName(dev), err)) } continue } if err := m.Curtail(*curtail); err == nil { site.log.DEBUG.Printf("%s curtail: %t", deviceTitleOrName(dev), *curtail) } else if !errors.Is(err, api.ErrNotAvailable) { errs = errors.Join(errs, fmt.Errorf("%s curtail: %w", deviceTitleOrName(dev), err)) } } return errs }