package charger import ( "context" "errors" "github.com/evcc-io/evcc/api" "github.com/evcc-io/evcc/api/implement" "github.com/evcc-io/evcc/charger/measurement" "github.com/evcc-io/evcc/core/loadpoint" "github.com/evcc-io/evcc/plugin" "github.com/evcc-io/evcc/util" ) func init() { registry.AddCtx("switchsocket", NewSwitchSocketFromConfig) } type SwitchSocket struct { implement.Caps enable func(bool) error enabled func() (bool, error) *switchSocket } func NewSwitchSocketFromConfig(ctx context.Context, other map[string]any) (api.Charger, error) { var cc struct { embed `mapstructure:",squash"` Enabled plugin.Config Enable plugin.Config Power *plugin.Config Energy *plugin.Config Soc *plugin.Config measurement.Temperature `mapstructure:",squash"` // optional, for heating devices StandbyPower float64 } if err := util.DecodeOther(other, &cc); err != nil { return nil, err } enabled, err := cc.Enabled.BoolGetter(ctx) if err != nil { return nil, err } enable, err := cc.Enable.BoolSetter(ctx, "enable") if err != nil { return nil, err } power, err := cc.Power.FloatGetter(ctx) if err != nil { return nil, err } // standbypower < 0 ensures that power is never used by the switch socket if not present if power == nil && cc.StandbyPower >= 0 { return nil, errors.New("missing either power or negative standbypower") } c := &SwitchSocket{ Caps: implement.New(), enabled: enabled, enable: enable, switchSocket: NewSwitchSocket(&cc.embed, enabled, power, cc.StandbyPower), } energy, err := cc.Energy.FloatGetter(ctx) if err != nil { return nil, err } implement.May(c, implement.MeterEnergy(energy)) soc, err := cc.Soc.FloatGetter(ctx) if err != nil { return nil, err } // for heating devices, the soc slot holds temperature in °C — fall back to temp getter temp, limitTemp, err := cc.Temperature.Configure(ctx) if err != nil { return nil, err } if soc == nil && temp != nil { soc = temp } implement.May(c, implement.Battery(soc)) implement.May(c, implement.SocLimiter(limitTemp)) return c, nil } func (c *SwitchSocket) Enabled() (bool, error) { return c.enabled() } func (c *SwitchSocket) Enable(enable bool) error { return c.enable(enable) } // switchSocket implements the api.Charger Status and CurrentPower methods // using basic generic switch socket functions type switchSocket struct { *embed enabled func() (bool, error) currentPower func() (float64, error) standbypower float64 lp loadpoint.API } func NewSwitchSocket( embed *embed, enabled func() (bool, error), currentPower func() (float64, error), standbypower float64, ) *switchSocket { return &switchSocket{ embed: embed, enabled: enabled, currentPower: currentPower, standbypower: standbypower, } } // Status calculates a generic switches status func (c *switchSocket) Status() (api.ChargeStatus, error) { if c.lp != nil && c.lp.GetMode() == api.ModeOff { return api.StatusA, nil } res := api.StatusB // static mode if c.standbypower < 0 { on, err := c.enabled() if on { res = api.StatusC } return res, err } // standby power mode power, err := c.currentPower() if power > c.standbypower { res = api.StatusC } return res, err } // MaxCurrent implements the api.Charger interface func (c *switchSocket) MaxCurrent(current int64) error { return nil } var _ api.ChargerEx = (*switchSocket)(nil) // MaxCurrentMillis implements the api.ChargerEx interface func (c *switchSocket) MaxCurrentMillis(current float64) error { return nil } var _ api.Meter = (*switchSocket)(nil) // CurrentPower calculates a generic switches power func (c *switchSocket) CurrentPower() (float64, error) { var power float64 // set fix static power in static mode if c.standbypower < 0 { on, err := c.enabled() if on { power = -c.standbypower } return power, err } // ignore power in standby mode power, err := c.currentPower() if power <= c.standbypower { power = 0 } return power, err } var _ loadpoint.Controller = (*switchSocket)(nil) // LoadpointControl implements loadpoint.Controller func (c *switchSocket) LoadpointControl(lp loadpoint.API) { c.lp = lp } var _ api.PhaseDescriber = (*switchSocket)(nil) // Phases implements the api.PhasesDescriber interface func (v *switchSocket) Phases() int { return 1 }