chore: simplify implementing decorators (#28576)

This commit is contained in:
andig 2026-05-08 14:33:34 +02:00 • committed by GitHub
parent a2ce340210
commit 4b21185219
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
148 changed files with 1500 additions and 6358 deletions

57
api/implement/caps.go Normal file
View file

@ -0,0 +1,57 @@
package implement
import (
"reflect"
"github.com/evcc-io/evcc/api"
)
// Has registers impl as a capability on c. It panics if impl is nil.
func Has[T any](c Caps, impl T) {
typ := reflect.TypeFor[T]()
if isNil(impl) {
panic("implement: nil " + typ.String())
}
c.add(typ, impl)
}
// May registers impl as a capability on c. If impl is nil, it is silently ignored.
func May[T any](c Caps, impl T) {
if isNil(impl) {
return
}
c.add(reflect.TypeFor[T](), impl)
}
func isNil(v any) bool {
if v == nil {
return true
}
switch rv := reflect.ValueOf(v); rv.Kind() {
case reflect.Pointer, reflect.Interface, reflect.Map, reflect.Slice, reflect.Chan, reflect.Func:
return rv.IsNil()
}
return false
}
type Caps interface {
api.Capable
add(typ reflect.Type, impl any)
}
// New creates a capabilities store exposing the api.Capable interface
func New() Caps {
return make(caps)
}
type caps map[reflect.Type]any
// Capability implements the api.Capable interface
func (caps caps) Capability(typ reflect.Type) (any, bool) {
c, ok := caps[typ]
return c, ok
}
func (caps caps) add(typ reflect.Type, impl any) {
caps[typ] = impl
}

View file

@ -0,0 +1,81 @@
package implement
import (
"testing"
"github.com/evcc-io/evcc/api"
"github.com/stretchr/testify/assert"
)
type isCapable struct {
Caps
}
func TestHas(t *testing.T) {
s := &isCapable{
Caps: New(),
}
Has(s, Meter(func() (float64, error) {
return 0, nil
}))
mm, ok := api.Cap[api.Meter](s)
assert.True(t, ok)
v, err := mm.CurrentPower()
assert.NoError(t, err)
assert.Equal(t, 0.0, v)
}
func TestHasPanicsOnNil(t *testing.T) {
s := &isCapable{
Caps: New(),
}
assert.Panics(t, func() {
Has[api.Meter](s, nil)
})
}
func TestMayIgnoresNil(t *testing.T) {
s := &isCapable{
Caps: New(),
}
assert.NotPanics(t, func() {
May[api.Meter](s, nil)
})
_, ok := api.Cap[api.Meter](s)
assert.False(t, ok)
}
func TestMayRegistersNonNil(t *testing.T) {
s := &isCapable{
Caps: New(),
}
May(s, Meter(func() (float64, error) {
return 1.0, nil
}))
mm, ok := api.Cap[api.Meter](s)
assert.True(t, ok)
v, err := mm.CurrentPower()
assert.NoError(t, err)
assert.Equal(t, 1.0, v)
}
func TestMayIgnoresNilFuncConstructor(t *testing.T) {
s := &isCapable{
Caps: New(),
}
var fn func() (float64, error)
May(s, Meter(fn))
_, ok := api.Cap[api.Meter](s)
assert.False(t, ok)
}

View file

@ -0,0 +1,449 @@
package implement
// Code generated by github.com/evcc-io/evcc/api/implement/caps.go. DO NOT EDIT.
import (
"time"
"github.com/evcc-io/evcc/api"
)
func Meter(meter0 func() (float64, error)) api.Meter {
if meter0 == nil {
return nil
}
return &iMeter{meter0}
}
type iMeter struct {
meter0 func() (float64, error)
}
func (i *iMeter) CurrentPower() (float64, error) {
return i.meter0()
}
func BatteryCapacity(batteryCapacity0 func() float64) api.BatteryCapacity {
if batteryCapacity0 == nil {
return nil
}
return &iBatteryCapacity{batteryCapacity0}
}
type iBatteryCapacity struct {
batteryCapacity0 func() float64
}
func (i *iBatteryCapacity) Capacity() float64 {
return i.batteryCapacity0()
}
func SocLimiter(socLimiter0 func() (int64, error)) api.SocLimiter {
if socLimiter0 == nil {
return nil
}
return &iSocLimiter{socLimiter0}
}
type iSocLimiter struct {
socLimiter0 func() (int64, error)
}
func (i *iSocLimiter) GetLimitSoc() (int64, error) {
return i.socLimiter0()
}
func BatteryController(batteryController0 func(api.BatteryMode) error) api.BatteryController {
if batteryController0 == nil {
return nil
}
return &iBatteryController{batteryController0}
}
type iBatteryController struct {
batteryController0 func(api.BatteryMode) error
}
func (i *iBatteryController) SetBatteryMode(p0 api.BatteryMode) error {
return i.batteryController0(p0)
}
func BatterySocLimiter(batterySocLimiter0 func() (float64, float64)) api.BatterySocLimiter {
if batterySocLimiter0 == nil {
return nil
}
return &iBatterySocLimiter{batterySocLimiter0}
}
type iBatterySocLimiter struct {
batterySocLimiter0 func() (float64, float64)
}
func (i *iBatterySocLimiter) GetSocLimits() (float64, float64) {
return i.batterySocLimiter0()
}
func BatteryPowerLimiter(batteryPowerLimiter0 func() (float64, float64)) api.BatteryPowerLimiter {
if batteryPowerLimiter0 == nil {
return nil
}
return &iBatteryPowerLimiter{batteryPowerLimiter0}
}
type iBatteryPowerLimiter struct {
batteryPowerLimiter0 func() (float64, float64)
}
func (i *iBatteryPowerLimiter) GetPowerLimits() (float64, float64) {
return i.batteryPowerLimiter0()
}
func PhasePowers(phasePowers0 func() (float64, float64, float64, error)) api.PhasePowers {
if phasePowers0 == nil {
return nil
}
return &iPhasePowers{phasePowers0}
}
type iPhasePowers struct {
phasePowers0 func() (float64, float64, float64, error)
}
func (i *iPhasePowers) Powers() (float64, float64, float64, error) {
return i.phasePowers0()
}
func PhaseGetter(phaseGetter0 func() (int, error)) api.PhaseGetter {
if phaseGetter0 == nil {
return nil
}
return &iPhaseGetter{phaseGetter0}
}
type iPhaseGetter struct {
phaseGetter0 func() (int, error)
}
func (i *iPhaseGetter) GetPhases() (int, error) {
return i.phaseGetter0()
}
func ChargeController(chargeController0 func(bool) error) api.ChargeController {
if chargeController0 == nil {
return nil
}
return &iChargeController{chargeController0}
}
type iChargeController struct {
chargeController0 func(bool) error
}
func (i *iChargeController) ChargeEnable(p0 bool) error {
return i.chargeController0(p0)
}
func CurrentController(currentController0 func(int64) error) api.CurrentController {
if currentController0 == nil {
return nil
}
return &iCurrentController{currentController0}
}
type iCurrentController struct {
currentController0 func(int64) error
}
func (i *iCurrentController) MaxCurrent(p0 int64) error {
return i.currentController0(p0)
}
func PhaseSwitcher(phaseSwitcher0 func(int) error) api.PhaseSwitcher {
if phaseSwitcher0 == nil {
return nil
}
return &iPhaseSwitcher{phaseSwitcher0}
}
type iPhaseSwitcher struct {
phaseSwitcher0 func(int) error
}
func (i *iPhaseSwitcher) Phases1p3p(p0 int) error {
return i.phaseSwitcher0(p0)
}
func Battery(battery0 func() (float64, error)) api.Battery {
if battery0 == nil {
return nil
}
return &iBattery{battery0}
}
type iBattery struct {
battery0 func() (float64, error)
}
func (i *iBattery) Soc() (float64, error) {
return i.battery0()
}
func ChargeState(chargeState0 func() (api.ChargeStatus, error)) api.ChargeState {
if chargeState0 == nil {
return nil
}
return &iChargeState{chargeState0}
}
type iChargeState struct {
chargeState0 func() (api.ChargeStatus, error)
}
func (i *iChargeState) Status() (api.ChargeStatus, error) {
return i.chargeState0()
}
func MeterEnergy(meterEnergy0 func() (float64, error)) api.MeterEnergy {
if meterEnergy0 == nil {
return nil
}
return &iMeterEnergy{meterEnergy0}
}
type iMeterEnergy struct {
meterEnergy0 func() (float64, error)
}
func (i *iMeterEnergy) TotalEnergy() (float64, error) {
return i.meterEnergy0()
}
func PhaseCurrents(phaseCurrents0 func() (float64, float64, float64, error)) api.PhaseCurrents {
if phaseCurrents0 == nil {
return nil
}
return &iPhaseCurrents{phaseCurrents0}
}
type iPhaseCurrents struct {
phaseCurrents0 func() (float64, float64, float64, error)
}
func (i *iPhaseCurrents) Currents() (float64, float64, float64, error) {
return i.phaseCurrents0()
}
func PhaseVoltages(phaseVoltages0 func() (float64, float64, float64, error)) api.PhaseVoltages {
if phaseVoltages0 == nil {
return nil
}
return &iPhaseVoltages{phaseVoltages0}
}
type iPhaseVoltages struct {
phaseVoltages0 func() (float64, float64, float64, error)
}
func (i *iPhaseVoltages) Voltages() (float64, float64, float64, error) {
return i.phaseVoltages0()
}
func MaxACPowerGetter(maxACPowerGetter0 func() float64) api.MaxACPowerGetter {
if maxACPowerGetter0 == nil {
return nil
}
return &iMaxACPowerGetter{maxACPowerGetter0}
}
type iMaxACPowerGetter struct {
maxACPowerGetter0 func() float64
}
func (i *iMaxACPowerGetter) MaxACPower() float64 {
return i.maxACPowerGetter0()
}
func CurrentGetter(currentGetter0 func() (float64, error)) api.CurrentGetter {
if currentGetter0 == nil {
return nil
}
return &iCurrentGetter{currentGetter0}
}
type iCurrentGetter struct {
currentGetter0 func() (float64, error)
}
func (i *iCurrentGetter) GetMaxCurrent() (float64, error) {
return i.currentGetter0()
}
func Curtailer(curtailer0 func(bool) error, curtailer1 func() (bool, error)) api.Curtailer {
if curtailer0 == nil || curtailer1 == nil {
return nil
}
return &iCurtailer{curtailer0, curtailer1}
}
type iCurtailer struct {
curtailer0 func(bool) error
curtailer1 func() (bool, error)
}
func (i *iCurtailer) Curtail(p0 bool) error {
return i.curtailer0(p0)
}
func (i *iCurtailer) Curtailed() (bool, error) {
return i.curtailer1()
}
func Resurrector(resurrector0 func() error) api.Resurrector {
if resurrector0 == nil {
return nil
}
return &iResurrector{resurrector0}
}
type iResurrector struct {
resurrector0 func() error
}
func (i *iResurrector) WakeUp() error {
return i.resurrector0()
}
func VehicleOdometer(vehicleOdometer0 func() (float64, error)) api.VehicleOdometer {
if vehicleOdometer0 == nil {
return nil
}
return &iVehicleOdometer{vehicleOdometer0}
}
type iVehicleOdometer struct {
vehicleOdometer0 func() (float64, error)
}
func (i *iVehicleOdometer) Odometer() (float64, error) {
return i.vehicleOdometer0()
}
func VehicleRange(vehicleRange0 func() (int64, error)) api.VehicleRange {
if vehicleRange0 == nil {
return nil
}
return &iVehicleRange{vehicleRange0}
}
type iVehicleRange struct {
vehicleRange0 func() (int64, error)
}
func (i *iVehicleRange) Range() (int64, error) {
return i.vehicleRange0()
}
func VehicleClimater(vehicleClimater0 func() (bool, error)) api.VehicleClimater {
if vehicleClimater0 == nil {
return nil
}
return &iVehicleClimater{vehicleClimater0}
}
type iVehicleClimater struct {
vehicleClimater0 func() (bool, error)
}
func (i *iVehicleClimater) Climater() (bool, error) {
return i.vehicleClimater0()
}
func VehicleFinishTimer(vehicleFinishTimer0 func() (time.Time, error)) api.VehicleFinishTimer {
if vehicleFinishTimer0 == nil {
return nil
}
return &iVehicleFinishTimer{vehicleFinishTimer0}
}
type iVehicleFinishTimer struct {
vehicleFinishTimer0 func() (time.Time, error)
}
func (i *iVehicleFinishTimer) FinishTime() (time.Time, error) {
return i.vehicleFinishTimer0()
}
func VehiclePosition(vehiclePosition0 func() (float64, float64, error)) api.VehiclePosition {
if vehiclePosition0 == nil {
return nil
}
return &iVehiclePosition{vehiclePosition0}
}
type iVehiclePosition struct {
vehiclePosition0 func() (float64, float64, error)
}
func (i *iVehiclePosition) Position() (float64, float64, error) {
return i.vehiclePosition0()
}
func Identifier(identifier0 func() (string, error)) api.Identifier {
if identifier0 == nil {
return nil
}
return &iIdentifier{identifier0}
}
type iIdentifier struct {
identifier0 func() (string, error)
}
func (i *iIdentifier) Identify() (string, error) {
return i.identifier0()
}
func ChargerEx(chargerEx0 func(float64) error) api.ChargerEx {
if chargerEx0 == nil {
return nil
}
return &iChargerEx{chargerEx0}
}
type iChargerEx struct {
chargerEx0 func(float64) error
}
func (i *iChargerEx) MaxCurrentMillis(p0 float64) error {
return i.chargerEx0(p0)
}
func ChargeRater(chargeRater0 func() (float64, error)) api.ChargeRater {
if chargeRater0 == nil {
return nil
}
return &iChargeRater{chargeRater0}
}
type iChargeRater struct {
chargeRater0 func() (float64, error)
}
func (i *iChargeRater) ChargedEnergy() (float64, error) {
return i.chargeRater0()
}
func StatusReasoner(statusReasoner0 func() (api.Reason, error)) api.StatusReasoner {
if statusReasoner0 == nil {
return nil
}
return &iStatusReasoner{statusReasoner0}
}
type iStatusReasoner struct {
statusReasoner0 func() (api.Reason, error)
}
func (i *iStatusReasoner) StatusReason() (api.Reason, error) {
return i.statusReasoner0()
}

View file

@ -24,6 +24,7 @@ import (
"time"
"github.com/evcc-io/evcc/api"
"github.com/evcc-io/evcc/api/implement"
"github.com/evcc-io/evcc/util"
"github.com/evcc-io/evcc/util/modbus"
"github.com/evcc-io/evcc/util/sponsor"
@ -31,6 +32,7 @@ import (
// ABLeMH charger implementation
type ABLeMH struct {
implement.Caps
conn *modbus.Connection
curr uint16
}
@ -96,8 +98,6 @@ func NewABLeMHFromConfig(ctx context.Context, other map[string]any) (api.Charger
return NewABLeMH(ctx, cc.URI, cc.Device, cc.Comset, cc.Baudrate, cc.ID, cc.Timeout)
}
//go:generate go tool decorate -f decorateABLeMH -b *ABLeMH -r api.Charger -t api.Meter,api.PhaseCurrents
// NewABLeMH creates ABLeMH charger
func NewABLeMH(ctx context.Context, uri, device, comset string, baudrate int, slaveID uint8, timeout time.Duration) (api.Charger, error) {
conn, err := modbus.NewConnection(ctx, uri, device, comset, baudrate, modbus.Ascii, slaveID)
@ -117,6 +117,7 @@ func NewABLeMH(ctx context.Context, uri, device, comset string, baudrate int, sl
conn.Logger(log.TRACE)
wb := &ABLeMH{
Caps: implement.New(),
conn: conn,
curr: uint16(6 / 0.06),
}
@ -125,7 +126,8 @@ func NewABLeMH(ctx context.Context, uri, device, comset string, baudrate int, sl
// check presence of current sensor
if err == nil && (b[3]&ablSensorPresent != 0) {
return decorateABLeMH(wb, wb.currentPower, wb.currents), nil
implement.Has(wb, implement.Meter(wb.currentPower))
implement.Has(wb, implement.PhaseCurrents(wb.currents))
}
return wb, nil

View file

@ -1,56 +0,0 @@
package charger
// Code generated by github.com/evcc-io/evcc/cmd/tools/decorate.go. DO NOT EDIT.
import (
"reflect"
"github.com/evcc-io/evcc/api"
)
func decorateABLeMH(base *ABLeMH, meter func() (float64, error), phaseCurrents func() (float64, float64, float64, error)) api.Charger {
caps := make(map[reflect.Type]any)
if meter != nil {
caps[reflect.TypeFor[api.Meter]()] = &decorateABLeMHMeterImpl{meter: meter}
}
if phaseCurrents != nil {
caps[reflect.TypeFor[api.PhaseCurrents]()] = &decorateABLeMHPhaseCurrentsImpl{phaseCurrents: phaseCurrents}
}
if len(caps) == 0 {
return base
}
return &decorateABLeMHCapable{ABLeMH: base, caps: caps}
}
type decorateABLeMHCapable struct {
*ABLeMH
caps map[reflect.Type]any
}
func (d *decorateABLeMHCapable) Capability(typ reflect.Type) (any, bool) {
c, ok := d.caps[typ]
if !ok && reflect.TypeOf(d).Implements(typ) {
return d, true
}
return c, ok
}
type decorateABLeMHMeterImpl struct {
meter func() (float64, error)
}
func (impl *decorateABLeMHMeterImpl) CurrentPower() (float64, error) {
return impl.meter()
}
type decorateABLeMHPhaseCurrentsImpl struct {
phaseCurrents func() (float64, float64, float64, error)
}
func (impl *decorateABLeMHPhaseCurrentsImpl) Currents() (float64, float64, float64, error) {
return impl.phaseCurrents()
}

View file

@ -25,6 +25,7 @@ import (
"time"
"github.com/evcc-io/evcc/api"
"github.com/evcc-io/evcc/api/implement"
"github.com/evcc-io/evcc/util"
"github.com/evcc-io/evcc/util/modbus"
"github.com/evcc-io/evcc/util/sponsor"
@ -35,6 +36,7 @@ import (
// Alfen charger implementation
type Alfen struct {
implement.Caps
log *util.Logger
conn *modbus.Connection
mu sync.Mutex
@ -56,8 +58,6 @@ func init() {
registry.AddCtx("alfen", NewAlfenFromConfig)
}
//go:generate go tool decorate -f decorateAlfen -b *Alfen -r api.Charger -t api.PhaseSwitcher,api.PhaseGetter
// NewAlfenFromConfig creates a Alfen charger from generic config
func NewAlfenFromConfig(ctx context.Context, other map[string]any) (api.Charger, error) {
cc := modbus.TcpSettings{
@ -86,6 +86,7 @@ func NewAlfen(ctx context.Context, uri string, slaveID uint8) (api.Charger, erro
conn.Logger(log.TRACE)
wb := &Alfen{
Caps: implement.New(),
log: log,
conn: conn,
}
@ -97,19 +98,15 @@ func NewAlfen(ctx context.Context, uri string, slaveID uint8) (api.Charger, erro
return nil, err
}
var (
phasesS func(int) error
phasesG func() (int, error)
)
if v2 != 0 && v3 != 0 {
wb.log.DEBUG.Println("detected 3p alfen")
phasesS = wb.phases1p3p
phasesG = wb.getPhases
implement.Has(wb, implement.PhaseSwitcher(wb.phases1p3p))
implement.Has(wb, implement.PhaseGetter(wb.getPhases))
} else {
wb.log.DEBUG.Println("detected 1p alfen")
}
return decorateAlfen(wb, phasesS, phasesG), nil
return wb, nil
}
func (wb *Alfen) heartbeat(ctx context.Context) {

View file

@ -1,56 +0,0 @@
package charger
// Code generated by github.com/evcc-io/evcc/cmd/tools/decorate.go. DO NOT EDIT.
import (
"reflect"
"github.com/evcc-io/evcc/api"
)
func decorateAlfen(base *Alfen, phaseSwitcher func(int) error, phaseGetter func() (int, error)) api.Charger {
caps := make(map[reflect.Type]any)
if phaseSwitcher != nil {
caps[reflect.TypeFor[api.PhaseSwitcher]()] = &decorateAlfenPhaseSwitcherImpl{phaseSwitcher: phaseSwitcher}
}
if phaseGetter != nil {
caps[reflect.TypeFor[api.PhaseGetter]()] = &decorateAlfenPhaseGetterImpl{phaseGetter: phaseGetter}
}
if len(caps) == 0 {
return base
}
return &decorateAlfenCapable{Alfen: base, caps: caps}
}
type decorateAlfenCapable struct {
*Alfen
caps map[reflect.Type]any
}
func (d *decorateAlfenCapable) Capability(typ reflect.Type) (any, bool) {
c, ok := d.caps[typ]
if !ok && reflect.TypeOf(d).Implements(typ) {
return d, true
}
return c, ok
}
type decorateAlfenPhaseGetterImpl struct {
phaseGetter func() (int, error)
}
func (impl *decorateAlfenPhaseGetterImpl) GetPhases() (int, error) {
return impl.phaseGetter()
}
type decorateAlfenPhaseSwitcherImpl struct {
phaseSwitcher func(int) error
}
func (impl *decorateAlfenPhaseSwitcherImpl) Phases1p3p(p0 int) error {
return impl.phaseSwitcher(p0)
}

View file

@ -25,6 +25,7 @@ import (
"time"
"github.com/evcc-io/evcc/api"
"github.com/evcc-io/evcc/api/implement"
"github.com/evcc-io/evcc/util"
"github.com/evcc-io/evcc/util/modbus"
"github.com/evcc-io/evcc/util/sponsor"
@ -32,6 +33,7 @@ import (
// Amperfied charger implementation
type Amperfied struct {
implement.Caps
log *util.Logger
conn *modbus.Connection
current uint16
@ -59,8 +61,6 @@ func init() {
registry.AddCtx("amperfied", NewAmperfiedFromConfig)
}
//go:generate go tool decorate -f decorateAmperfied -b *Amperfied -r api.Charger -t api.PhaseSwitcher,api.PhaseGetter
// NewAmperfiedFromConfig creates a Amperfied charger from generic config
func NewAmperfiedFromConfig(ctx context.Context, other map[string]any) (api.Charger, error) {
cc := struct {
@ -94,6 +94,7 @@ func NewAmperfied(ctx context.Context, uri string, slaveID uint8, phases bool) (
conn.Logger(log.TRACE)
wb := &Amperfied{
Caps: implement.New(),
log: log,
conn: conn,
current: 60, // assume min current
@ -108,14 +109,12 @@ func NewAmperfied(ctx context.Context, uri string, slaveID uint8, phases bool) (
go wb.heartbeat(ctx, time.Duration(u)*time.Millisecond/2)
}
var phases1p3p func(int) error
var phasesG func() (int, error)
if phases {
phases1p3p = wb.phases1p3p
phasesG = wb.getPhases
implement.Has(wb, implement.PhaseSwitcher(wb.phases1p3p))
implement.Has(wb, implement.PhaseGetter(wb.getPhases))
}
return decorateAmperfied(wb, phases1p3p, phasesG), nil
return wb, nil
}
func (wb *Amperfied) heartbeat(ctx context.Context, timeout time.Duration) {

View file

@ -1,56 +0,0 @@
package charger
// Code generated by github.com/evcc-io/evcc/cmd/tools/decorate.go. DO NOT EDIT.
import (
"reflect"
"github.com/evcc-io/evcc/api"
)
func decorateAmperfied(base *Amperfied, phaseSwitcher func(int) error, phaseGetter func() (int, error)) api.Charger {
caps := make(map[reflect.Type]any)
if phaseSwitcher != nil {
caps[reflect.TypeFor[api.PhaseSwitcher]()] = &decorateAmperfiedPhaseSwitcherImpl{phaseSwitcher: phaseSwitcher}
}
if phaseGetter != nil {
caps[reflect.TypeFor[api.PhaseGetter]()] = &decorateAmperfiedPhaseGetterImpl{phaseGetter: phaseGetter}
}
if len(caps) == 0 {
return base
}
return &decorateAmperfiedCapable{Amperfied: base, caps: caps}
}
type decorateAmperfiedCapable struct {
*Amperfied
caps map[reflect.Type]any
}
func (d *decorateAmperfiedCapable) Capability(typ reflect.Type) (any, bool) {
c, ok := d.caps[typ]
if !ok && reflect.TypeOf(d).Implements(typ) {
return d, true
}
return c, ok
}
type decorateAmperfiedPhaseGetterImpl struct {
phaseGetter func() (int, error)
}
func (impl *decorateAmperfiedPhaseGetterImpl) GetPhases() (int, error) {
return impl.phaseGetter()
}
type decorateAmperfiedPhaseSwitcherImpl struct {
phaseSwitcher func(int) error
}
func (impl *decorateAmperfiedPhaseSwitcherImpl) Phases1p3p(p0 int) error {
return impl.phaseSwitcher(p0)
}

View file

@ -32,6 +32,7 @@ import (
"time"
"github.com/evcc-io/evcc/api"
"github.com/evcc-io/evcc/api/implement"
"github.com/evcc-io/evcc/charger/semp"
"github.com/evcc-io/evcc/util"
"github.com/evcc-io/evcc/util/modbus"
@ -47,6 +48,7 @@ type sempHandler struct {
// BenderCC charger implementation
type BenderCC struct {
implement.Caps
conn *modbus.Connection
current uint16
regCurr uint16
@ -111,8 +113,6 @@ func NewBenderCCFromConfig(ctx context.Context, other map[string]any) (api.Charg
}
// NewBenderCC creates BenderCC charger
//
//go:generate go tool decorate -f decorateBenderCC -b *BenderCC -r api.Charger -t api.Meter,api.PhaseCurrents,api.PhaseVoltages,api.MeterEnergy,api.Battery,api.Identifier,api.ChargerEx,api.PhaseSwitcher,api.PhaseGetter
func NewBenderCC(ctx context.Context, uri string, id uint8, cache time.Duration) (api.Charger, error) {
conn, err := modbus.NewConnection(ctx, uri, "", "", 0, modbus.Tcp, id)
if err != nil {
@ -127,6 +127,7 @@ func NewBenderCC(ctx context.Context, uri string, id uint8, cache time.Duration)
conn.Logger(log.TRACE)
wb := &BenderCC{
Caps: implement.New(),
conn: conn,
current: 6, // assume min current
regCurr: bendRegHemsCurrentLimit,
@ -138,18 +139,6 @@ func NewBenderCC(ctx context.Context, uri string, id uint8, cache time.Duration)
wb.legacy = true
}
var (
currentPower func() (float64, error)
currents func() (float64, float64, float64, error)
voltages func() (float64, float64, float64, error)
totalEnergy func() (float64, error)
soc func() (float64, error)
identify func() (string, error)
maxCurrentMillis func(float64) error
phases1p3p func(int) error
getPhases func() (int, error)
)
// check presence of metering
reg := uint16(bendRegActivePower)
if wb.legacy {
@ -157,41 +146,39 @@ func NewBenderCC(ctx context.Context, uri string, id uint8, cache time.Duration)
}
if b, err := wb.conn.ReadHoldingRegisters(reg, 2); err == nil && binary.BigEndian.Uint32(b) != math.MaxUint32 {
currentPower = wb.currentPower
currents = wb.currents
totalEnergy = wb.totalEnergy
implement.Has(wb, implement.Meter(wb.currentPower))
implement.Has(wb, implement.PhaseCurrents(wb.currents))
implement.Has(wb, implement.MeterEnergy(wb.totalEnergy))
// check presence of "ocpp meter"
if b, err := wb.conn.ReadHoldingRegisters(bendRegVoltages, 2); err == nil && binary.BigEndian.Uint32(b) > 0 {
voltages = wb.voltages
implement.Has(wb, implement.PhaseVoltages(wb.voltages))
}
if !wb.legacy {
if _, err := wb.conn.ReadHoldingRegisters(bendRegEVBatteryState, 1); err == nil {
soc = wb.soc
implement.Has(wb, implement.Battery(wb.soc))
}
}
}
// check feature mA
if _, err := wb.conn.ReadHoldingRegisters(bendRegHemsCurrentLimit10, 1); err == nil {
maxCurrentMillis = wb.maxCurrentMillis
implement.Has(wb, implement.ChargerEx(wb.maxCurrentMillis))
wb.regCurr = bendRegHemsCurrentLimit10
}
// check feature modbus power control/1p3p fpr Mennekes 4you / 4business chargers
// check feature modbus power control/1p3p for Mennekes 4you / 4business chargers
if _, err := wb.conn.ReadHoldingRegisters(bendRegHemsPowerLimit, 1); err == nil {
phases1p3p = wb.phases1p3pMennekes
getPhases = wb.getPhasesMennekes
}
implement.Has(wb, implement.PhaseSwitcher(wb.phases1p3pMennekes))
implement.Has(wb, implement.PhaseGetter(wb.getPhasesMennekes))
} else {
// check feature semp phase switching
if phases1p3p == nil {
if wb.supportsSEMPPhaseSwitching(uri, cache) {
// set initial SEMP power limit to max so modbus control from 6 to 16 A is possible
if err := wb.semp.conn.SendDeviceControl(wb.semp.deviceID, 0xffff); err == nil {
phases1p3p = wb.phases1p3pSEMP
getPhases = wb.getPhases
implement.Has(wb, implement.PhaseSwitcher(wb.phases1p3pSEMP))
implement.Has(wb, implement.PhaseGetter(wb.getPhases))
// start heartbeat to keep connection alive
go wb.heartbeat(ctx)
} else {
@ -202,10 +189,10 @@ func NewBenderCC(ctx context.Context, uri string, id uint8, cache time.Duration)
// check feature rfid
if _, err := wb.identify(); err == nil {
identify = wb.identify
implement.Has(wb, implement.Identifier(wb.identify))
}
return decorateBenderCC(wb, currentPower, currents, voltages, totalEnergy, soc, identify, maxCurrentMillis, phases1p3p, getPhases), nil
return wb, nil
}
// heartbeat ensures that SEMP device control updates are sent about once per minute

View file

@ -1,140 +0,0 @@
package charger
// Code generated by github.com/evcc-io/evcc/cmd/tools/decorate.go. DO NOT EDIT.
import (
"reflect"
"github.com/evcc-io/evcc/api"
)
func decorateBenderCC(base *BenderCC, meter func() (float64, error), phaseCurrents func() (float64, float64, float64, error), phaseVoltages func() (float64, float64, float64, error), meterEnergy func() (float64, error), battery func() (float64, error), identifier func() (string, error), chargerEx func(float64) error, phaseSwitcher func(int) error, phaseGetter func() (int, error)) api.Charger {
caps := make(map[reflect.Type]any)
if meter != nil {
caps[reflect.TypeFor[api.Meter]()] = &decorateBenderCCMeterImpl{meter: meter}
}
if phaseCurrents != nil {
caps[reflect.TypeFor[api.PhaseCurrents]()] = &decorateBenderCCPhaseCurrentsImpl{phaseCurrents: phaseCurrents}
}
if phaseVoltages != nil {
caps[reflect.TypeFor[api.PhaseVoltages]()] = &decorateBenderCCPhaseVoltagesImpl{phaseVoltages: phaseVoltages}
}
if meterEnergy != nil {
caps[reflect.TypeFor[api.MeterEnergy]()] = &decorateBenderCCMeterEnergyImpl{meterEnergy: meterEnergy}
}
if battery != nil {
caps[reflect.TypeFor[api.Battery]()] = &decorateBenderCCBatteryImpl{battery: battery}
}
if identifier != nil {
caps[reflect.TypeFor[api.Identifier]()] = &decorateBenderCCIdentifierImpl{identifier: identifier}
}
if chargerEx != nil {
caps[reflect.TypeFor[api.ChargerEx]()] = &decorateBenderCCChargerExImpl{chargerEx: chargerEx}
}
if phaseSwitcher != nil {
caps[reflect.TypeFor[api.PhaseSwitcher]()] = &decorateBenderCCPhaseSwitcherImpl{phaseSwitcher: phaseSwitcher}
}
if phaseGetter != nil {
caps[reflect.TypeFor[api.PhaseGetter]()] = &decorateBenderCCPhaseGetterImpl{phaseGetter: phaseGetter}
}
if len(caps) == 0 {
return base
}
return &decorateBenderCCCapable{BenderCC: base, caps: caps}
}
type decorateBenderCCCapable struct {
*BenderCC
caps map[reflect.Type]any
}
func (d *decorateBenderCCCapable) Capability(typ reflect.Type) (any, bool) {
c, ok := d.caps[typ]
if !ok && reflect.TypeOf(d).Implements(typ) {
return d, true
}
return c, ok
}
type decorateBenderCCBatteryImpl struct {
battery func() (float64, error)
}
func (impl *decorateBenderCCBatteryImpl) Soc() (float64, error) {
return impl.battery()
}
type decorateBenderCCChargerExImpl struct {
chargerEx func(float64) error
}
func (impl *decorateBenderCCChargerExImpl) MaxCurrentMillis(p0 float64) error {
return impl.chargerEx(p0)
}
type decorateBenderCCIdentifierImpl struct {
identifier func() (string, error)
}
func (impl *decorateBenderCCIdentifierImpl) Identify() (string, error) {
return impl.identifier()
}
type decorateBenderCCMeterImpl struct {
meter func() (float64, error)
}
func (impl *decorateBenderCCMeterImpl) CurrentPower() (float64, error) {
return impl.meter()
}
type decorateBenderCCMeterEnergyImpl struct {
meterEnergy func() (float64, error)
}
func (impl *decorateBenderCCMeterEnergyImpl) TotalEnergy() (float64, error) {
return impl.meterEnergy()
}
type decorateBenderCCPhaseCurrentsImpl struct {
phaseCurrents func() (float64, float64, float64, error)
}
func (impl *decorateBenderCCPhaseCurrentsImpl) Currents() (float64, float64, float64, error) {
return impl.phaseCurrents()
}
type decorateBenderCCPhaseGetterImpl struct {
phaseGetter func() (int, error)
}
func (impl *decorateBenderCCPhaseGetterImpl) GetPhases() (int, error) {
return impl.phaseGetter()
}
type decorateBenderCCPhaseSwitcherImpl struct {
phaseSwitcher func(int) error
}
func (impl *decorateBenderCCPhaseSwitcherImpl) Phases1p3p(p0 int) error {
return impl.phaseSwitcher(p0)
}
type decorateBenderCCPhaseVoltagesImpl struct {
phaseVoltages func() (float64, float64, float64, error)
}
func (impl *decorateBenderCCPhaseVoltagesImpl) Voltages() (float64, float64, float64, error) {
return impl.phaseVoltages()
}

View file

@ -6,6 +6,7 @@ import (
"fmt"
"github.com/evcc-io/evcc/api"
"github.com/evcc-io/evcc/api/implement"
"github.com/evcc-io/evcc/util"
"github.com/evcc-io/evcc/util/modbus"
"github.com/evcc-io/evcc/util/sponsor"
@ -31,6 +32,7 @@ const (
// It uses Modbus TCP to communicate at modbus client id 1 and power meters at id 2 and 3.
// https://www.cfos-emobility.de/en-gb/cfos-power-brain/modbus-registers.htm
type CfosPowerBrain struct {
implement.Caps
conn *modbus.Connection
}
@ -38,8 +40,6 @@ func init() {
registry.AddCtx("cfos", NewCfosPowerBrainFromConfig)
}
//go:generate go tool decorate -f decorateCfos -b *CfosPowerBrain -r api.Charger -t api.Meter,api.MeterEnergy,api.PhaseCurrents,api.PhaseSwitcher
// NewCfosPowerBrainFromConfig creates a cFos charger from generic config
func NewCfosPowerBrainFromConfig(ctx context.Context, other map[string]any) (api.Charger, error) {
cc := modbus.TcpSettings{
@ -70,30 +70,26 @@ func NewCfosPowerBrain(ctx context.Context, uri string, id uint8) (api.Charger,
conn.Logger(log.TRACE)
wb := &CfosPowerBrain{
Caps: implement.New(),
conn: conn,
}
// decorate meter
var (
power, energy func() (float64, error)
currents func() (float64, float64, float64, error)
)
if b, err := wb.conn.ReadHoldingRegisters(cfosRegMeter, 1); err == nil && binary.BigEndian.Uint16(b) != 0 {
power = wb.currentPower
energy = wb.totalEnergy
implement.Has(wb, implement.Meter(wb.currentPower))
implement.Has(wb, implement.MeterEnergy(wb.totalEnergy))
if b, err := wb.conn.ReadHoldingRegisters(cfosRegMeterFlags, 1); err == nil && binary.BigEndian.Uint16(b) != 0 {
currents = wb.currents
implement.Has(wb, implement.PhaseCurrents(wb.currents))
}
}
// decorate phases
var phases1p3p func(int) error
if b, err := wb.conn.ReadHoldingRegisters(cfosRegSolarEnabled, 1); err == nil && binary.BigEndian.Uint16(b)&(1<<8) != 0 {
phases1p3p = wb.phases1p3p
implement.Has(wb, implement.PhaseSwitcher(wb.phases1p3p))
}
return decorateCfos(wb, power, energy, currents, phases1p3p), nil
return wb, nil
}
// Status implements the api.Charger interface

View file

@ -1,80 +0,0 @@
package charger
// Code generated by github.com/evcc-io/evcc/cmd/tools/decorate.go. DO NOT EDIT.
import (
"reflect"
"github.com/evcc-io/evcc/api"
)
func decorateCfos(base *CfosPowerBrain, meter func() (float64, error), meterEnergy func() (float64, error), phaseCurrents func() (float64, float64, float64, error), phaseSwitcher func(int) error) api.Charger {
caps := make(map[reflect.Type]any)
if meter != nil {
caps[reflect.TypeFor[api.Meter]()] = &decorateCfosMeterImpl{meter: meter}
}
if meterEnergy != nil {
caps[reflect.TypeFor[api.MeterEnergy]()] = &decorateCfosMeterEnergyImpl{meterEnergy: meterEnergy}
}
if phaseCurrents != nil {
caps[reflect.TypeFor[api.PhaseCurrents]()] = &decorateCfosPhaseCurrentsImpl{phaseCurrents: phaseCurrents}
}
if phaseSwitcher != nil {
caps[reflect.TypeFor[api.PhaseSwitcher]()] = &decorateCfosPhaseSwitcherImpl{phaseSwitcher: phaseSwitcher}
}
if len(caps) == 0 {
return base
}
return &decorateCfosCapable{CfosPowerBrain: base, caps: caps}
}
type decorateCfosCapable struct {
*CfosPowerBrain
caps map[reflect.Type]any
}
func (d *decorateCfosCapable) Capability(typ reflect.Type) (any, bool) {
c, ok := d.caps[typ]
if !ok && reflect.TypeOf(d).Implements(typ) {
return d, true
}
return c, ok
}
type decorateCfosMeterImpl struct {
meter func() (float64, error)
}
func (impl *decorateCfosMeterImpl) CurrentPower() (float64, error) {
return impl.meter()
}
type decorateCfosMeterEnergyImpl struct {
meterEnergy func() (float64, error)
}
func (impl *decorateCfosMeterEnergyImpl) TotalEnergy() (float64, error) {
return impl.meterEnergy()
}
type decorateCfosPhaseCurrentsImpl struct {
phaseCurrents func() (float64, float64, float64, error)
}
func (impl *decorateCfosPhaseCurrentsImpl) Currents() (float64, float64, float64, error) {
return impl.phaseCurrents()
}
type decorateCfosPhaseSwitcherImpl struct {
phaseSwitcher func(int) error
}
func (impl *decorateCfosPhaseSwitcherImpl) Phases1p3p(p0 int) error {
return impl.phaseSwitcher(p0)
}

View file

@ -4,9 +4,9 @@ import (
"context"
"errors"
"fmt"
"time"
"github.com/evcc-io/evcc/api"
"github.com/evcc-io/evcc/api/implement"
"github.com/evcc-io/evcc/charger/measurement"
meter "github.com/evcc-io/evcc/meter/measurement"
"github.com/evcc-io/evcc/plugin"
@ -16,6 +16,7 @@ import (
// Charger is an api.Charger implementation with configurable getters and setters.
type Charger struct {
*embed
implement.Caps
statusG func() (string, error)
enabledG func() (bool, error)
enableS func(bool) error
@ -26,8 +27,6 @@ func init() {
registry.AddCtx(api.Custom, NewConfigurableFromConfig)
}
//go:generate go tool decorate -f decorateCustom -b *Charger -r api.Charger -t api.ChargerEx,api.Identifier,api.PhaseSwitcher,api.Resurrector,api.Battery,api.SocLimiter,api.Meter,api.MeterEnergy,api.PhaseCurrents,api.PhaseVoltages,api.VehicleFinishTimer
// NewConfigurableFromConfig creates a new charger from config
func NewConfigurableFromConfig(ctx context.Context, other map[string]any) (api.Charger, error) {
var cc struct {
@ -75,14 +74,15 @@ func NewConfigurableFromConfig(ctx context.Context, other map[string]any) (api.C
}
c.embed = &cc.embed
c.Caps = implement.New()
maxcurrentmillis, err := cc.MaxCurrentMillis.FloatSetter(ctx, "maxcurrentmillis")
if err != nil {
return nil, fmt.Errorf("maxcurrentmillis: %w", err)
}
implement.May(c, implement.ChargerEx(maxcurrentmillis))
// decorate phases
var phases1p3p func(int) error
if cc.Phases1p3p != nil {
if !cc.Tos {
return nil, errors.New("1p3p does no longer handle disable/enable. Use tos: true to confirm you understand the consequences")
@ -93,9 +93,9 @@ func NewConfigurableFromConfig(ctx context.Context, other map[string]any) (api.C
return nil, fmt.Errorf("phases: %w", err)
}
phases1p3p = func(phases int) error {
implement.Has(c, implement.PhaseSwitcher(func(phases int) error {
return phases1p3pS(int64(phases))
}
}))
}
// decorate identifier
@ -103,18 +103,18 @@ func NewConfigurableFromConfig(ctx context.Context, other map[string]any) (api.C
if err != nil {
return nil, fmt.Errorf("identify: %w", err)
}
implement.May(c, implement.Identifier(identify))
// decorate wakeup
var wakeup func() error
if cc.Wakeup != nil {
set, err := cc.Wakeup.BoolSetter(ctx, "wakeup")
wakeup, err := cc.Wakeup.BoolSetter(ctx, "wakeup")
if err != nil {
return nil, fmt.Errorf("wakeup: %w", err)
}
wakeup = func() error {
return set(true)
}
implement.Has(c, implement.Resurrector(func() error {
return wakeup(true)
}))
}
// decorate soc; for heating devices (api.Heating feature), the soc slot holds
@ -141,28 +141,32 @@ func NewConfigurableFromConfig(ctx context.Context, other map[string]any) (api.C
if limitsoc == nil && limitTemp != nil {
limitsoc = limitTemp
}
implement.May(c, implement.Battery(soc))
implement.May(c, implement.SocLimiter(limitsoc))
// decorate measurements
powerG, energyG, err := cc.Energy.Configure(ctx)
if err != nil {
return nil, err
}
implement.May(c, implement.Meter(powerG))
implement.May(c, implement.MeterEnergy(energyG))
currentsG, voltagesG, _, err := cc.Phases.Configure(ctx)
if err != nil {
return nil, err
}
implement.May(c, implement.PhaseCurrents(currentsG))
implement.May(c, implement.PhaseVoltages(voltagesG))
// decorate finishtime
var finishTime func() (time.Time, error)
if cc.FinishTime != nil {
finishTime, err = cc.FinishTime.TimeGetter(ctx)
finishTime, err := cc.FinishTime.TimeGetter(ctx)
if err != nil {
return nil, fmt.Errorf("finishTime: %w", err)
}
}
implement.May(c, implement.VehicleFinishTimer(finishTime))
return decorateCustom(c, maxcurrentmillis, identify, phases1p3p, wakeup, soc, limitsoc, powerG, energyG, currentsG, voltagesG, finishTime), nil
return c, nil
}
// NewConfigurable creates a new charger

View file

@ -1,165 +0,0 @@
package charger
// Code generated by github.com/evcc-io/evcc/cmd/tools/decorate.go. DO NOT EDIT.
import (
"reflect"
"time"
"github.com/evcc-io/evcc/api"
)
func decorateCustom(base *Charger, chargerEx func(float64) error, identifier func() (string, error), phaseSwitcher func(int) error, resurrector func() error, battery func() (float64, error), socLimiter func() (int64, error), meter func() (float64, error), meterEnergy func() (float64, error), phaseCurrents func() (float64, float64, float64, error), phaseVoltages func() (float64, float64, float64, error), vehicleFinishTimer func() (time.Time, error)) api.Charger {
caps := make(map[reflect.Type]any)
if chargerEx != nil {
caps[reflect.TypeFor[api.ChargerEx]()] = &decorateCustomChargerExImpl{chargerEx: chargerEx}
}
if identifier != nil {
caps[reflect.TypeFor[api.Identifier]()] = &decorateCustomIdentifierImpl{identifier: identifier}
}
if phaseSwitcher != nil {
caps[reflect.TypeFor[api.PhaseSwitcher]()] = &decorateCustomPhaseSwitcherImpl{phaseSwitcher: phaseSwitcher}
}
if resurrector != nil {
caps[reflect.TypeFor[api.Resurrector]()] = &decorateCustomResurrectorImpl{resurrector: resurrector}
}
if battery != nil {
caps[reflect.TypeFor[api.Battery]()] = &decorateCustomBatteryImpl{battery: battery}
}
if socLimiter != nil {
caps[reflect.TypeFor[api.SocLimiter]()] = &decorateCustomSocLimiterImpl{socLimiter: socLimiter}
}
if meter != nil {
caps[reflect.TypeFor[api.Meter]()] = &decorateCustomMeterImpl{meter: meter}
}
if meterEnergy != nil {
caps[reflect.TypeFor[api.MeterEnergy]()] = &decorateCustomMeterEnergyImpl{meterEnergy: meterEnergy}
}
if phaseCurrents != nil {
caps[reflect.TypeFor[api.PhaseCurrents]()] = &decorateCustomPhaseCurrentsImpl{phaseCurrents: phaseCurrents}
}
if phaseVoltages != nil {
caps[reflect.TypeFor[api.PhaseVoltages]()] = &decorateCustomPhaseVoltagesImpl{phaseVoltages: phaseVoltages}
}
if vehicleFinishTimer != nil {
caps[reflect.TypeFor[api.VehicleFinishTimer]()] = &decorateCustomVehicleFinishTimerImpl{vehicleFinishTimer: vehicleFinishTimer}
}
if len(caps) == 0 {
return base
}
return &decorateCustomCapable{Charger: base, caps: caps}
}
type decorateCustomCapable struct {
*Charger
caps map[reflect.Type]any
}
func (d *decorateCustomCapable) Capability(typ reflect.Type) (any, bool) {
c, ok := d.caps[typ]
if !ok && reflect.TypeOf(d).Implements(typ) {
return d, true
}
return c, ok
}
type decorateCustomBatteryImpl struct {
battery func() (float64, error)
}
func (impl *decorateCustomBatteryImpl) Soc() (float64, error) {
return impl.battery()
}
type decorateCustomChargerExImpl struct {
chargerEx func(float64) error
}
func (impl *decorateCustomChargerExImpl) MaxCurrentMillis(p0 float64) error {
return impl.chargerEx(p0)
}
type decorateCustomIdentifierImpl struct {
identifier func() (string, error)
}
func (impl *decorateCustomIdentifierImpl) Identify() (string, error) {
return impl.identifier()
}
type decorateCustomMeterImpl struct {
meter func() (float64, error)
}
func (impl *decorateCustomMeterImpl) CurrentPower() (float64, error) {
return impl.meter()
}
type decorateCustomMeterEnergyImpl struct {
meterEnergy func() (float64, error)
}
func (impl *decorateCustomMeterEnergyImpl) TotalEnergy() (float64, error) {
return impl.meterEnergy()
}
type decorateCustomPhaseCurrentsImpl struct {
phaseCurrents func() (float64, float64, float64, error)
}
func (impl *decorateCustomPhaseCurrentsImpl) Currents() (float64, float64, float64, error) {
return impl.phaseCurrents()
}
type decorateCustomPhaseSwitcherImpl struct {
phaseSwitcher func(int) error
}
func (impl *decorateCustomPhaseSwitcherImpl) Phases1p3p(p0 int) error {
return impl.phaseSwitcher(p0)
}
type decorateCustomPhaseVoltagesImpl struct {
phaseVoltages func() (float64, float64, float64, error)
}
func (impl *decorateCustomPhaseVoltagesImpl) Voltages() (float64, float64, float64, error) {
return impl.phaseVoltages()
}
type decorateCustomResurrectorImpl struct {
resurrector func() error
}
func (impl *decorateCustomResurrectorImpl) WakeUp() error {
return impl.resurrector()
}
type decorateCustomSocLimiterImpl struct {
socLimiter func() (int64, error)
}
func (impl *decorateCustomSocLimiterImpl) GetLimitSoc() (int64, error) {
return impl.socLimiter()
}
type decorateCustomVehicleFinishTimerImpl struct {
vehicleFinishTimer func() (time.Time, error)
}
func (impl *decorateCustomVehicleFinishTimerImpl) FinishTime() (time.Time, error) {
return impl.vehicleFinishTimer()
}

View file

@ -24,12 +24,14 @@ import (
"time"
"github.com/evcc-io/evcc/api"
"github.com/evcc-io/evcc/api/implement"
"github.com/evcc-io/evcc/util"
"github.com/evcc-io/evcc/util/modbus"
)
// DaheimLaden charger implementation
type DaheimLaden struct {
implement.Caps
log *util.Logger
conn *modbus.Connection
curr uint16
@ -65,8 +67,6 @@ func init() {
registry.AddCtx("daheimladen", NewDaheimLadenFromConfig)
}
//go:generate go tool decorate -f decorateDaheimLaden -b *DaheimLaden -r api.Charger -t api.PhaseSwitcher,api.PhaseGetter
// NewDaheimLadenFromConfig creates a DaheimLaden charger from generic config
func NewDaheimLadenFromConfig(ctx context.Context, other map[string]any) (api.Charger, error) {
cc := struct {
@ -96,6 +96,7 @@ func NewDaheimLaden(ctx context.Context, uri string, id uint8, phases bool) (api
conn.Logger(log.TRACE)
wb := &DaheimLaden{
Caps: implement.New(),
log: log,
conn: conn,
curr: 60, // assume min current
@ -120,14 +121,12 @@ func NewDaheimLaden(ctx context.Context, uri string, id uint8, phases bool) (api
go wb.heartbeat(ctx, time.Duration(u)*time.Second/2)
}
var phases1p3p func(int) error
var phasesG func() (int, error)
if phases {
phases1p3p = wb.phases1p3p
phasesG = wb.getPhases
implement.Has(wb, implement.PhaseSwitcher(wb.phases1p3p))
implement.Has(wb, implement.PhaseGetter(wb.getPhases))
}
return decorateDaheimLaden(wb, phases1p3p, phasesG), nil
return wb, nil
}
func (wb *DaheimLaden) heartbeat(ctx context.Context, timeout time.Duration) {

View file

@ -1,56 +0,0 @@
package charger
// Code generated by github.com/evcc-io/evcc/cmd/tools/decorate.go. DO NOT EDIT.
import (
"reflect"
"github.com/evcc-io/evcc/api"
)
func decorateDaheimLaden(base *DaheimLaden, phaseSwitcher func(int) error, phaseGetter func() (int, error)) api.Charger {
caps := make(map[reflect.Type]any)
if phaseSwitcher != nil {
caps[reflect.TypeFor[api.PhaseSwitcher]()] = &decorateDaheimLadenPhaseSwitcherImpl{phaseSwitcher: phaseSwitcher}
}
if phaseGetter != nil {
caps[reflect.TypeFor[api.PhaseGetter]()] = &decorateDaheimLadenPhaseGetterImpl{phaseGetter: phaseGetter}
}
if len(caps) == 0 {
return base
}
return &decorateDaheimLadenCapable{DaheimLaden: base, caps: caps}
}
type decorateDaheimLadenCapable struct {
*DaheimLaden
caps map[reflect.Type]any
}
func (d *decorateDaheimLadenCapable) Capability(typ reflect.Type) (any, bool) {
c, ok := d.caps[typ]
if !ok && reflect.TypeOf(d).Implements(typ) {
return d, true
}
return c, ok
}
type decorateDaheimLadenPhaseGetterImpl struct {
phaseGetter func() (int, error)
}
func (impl *decorateDaheimLadenPhaseGetterImpl) GetPhases() (int, error) {
return impl.phaseGetter()
}
type decorateDaheimLadenPhaseSwitcherImpl struct {
phaseSwitcher func(int) error
}
func (impl *decorateDaheimLadenPhaseSwitcherImpl) Phases1p3p(p0 int) error {
return impl.phaseSwitcher(p0)
}

View file

@ -13,6 +13,7 @@ import (
"github.com/enbility/eebus-go/usecases/cem/evcem"
spineapi "github.com/enbility/spine-go/api"
"github.com/evcc-io/evcc/api"
"github.com/evcc-io/evcc/api/implement"
"github.com/evcc-io/evcc/core/loadpoint"
"github.com/evcc-io/evcc/server/eebus"
"github.com/evcc-io/evcc/util"
@ -29,6 +30,7 @@ type minMax struct {
}
type EEBus struct {
implement.Caps
cem *eebus.CustomerEnergyManagement
ev spineapi.EntityRemoteInterface
@ -69,8 +71,6 @@ func NewEEBusFromConfig(ctx context.Context, other map[string]any) (api.Charger,
return NewEEBus(ctx, cc.Ski, cc.Ip, cc.Meter, hasChargedEnergy)
}
//go:generate go tool decorate -f decorateEEBus -b *EEBus -r api.Charger -t api.Meter,api.PhaseCurrents,api.ChargeRater
// newEEBus creates and initializes a raw *EEBus charger.
// It registers the device with the EEBus instance and waits for the connection.
func newEEBus(ctx context.Context, ski, ip string) (*EEBus, error) {
@ -79,6 +79,7 @@ func newEEBus(ctx context.Context, ski, ip string) (*EEBus, error) {
}
c := &EEBus{
Caps: implement.New(),
log: util.NewLogger("eebus"),
current: 6,
cem: eebus.Instance.CustomerEnergyManagement(),
@ -113,11 +114,11 @@ func NewEEBus(ctx context.Context, ski, ip string, hasMeter, hasChargedEnergy bo
}
if hasMeter {
var energyG func() (float64, error)
implement.Has(c, implement.Meter(c.currentPower))
implement.Has(c, implement.PhaseCurrents(c.currents))
if hasChargedEnergy {
energyG = c.chargedEnergy
implement.Has(c, implement.ChargeRater(c.chargedEnergy))
}
return decorateEEBus(c, c.currentPower, c.currents, energyG), nil
}
return c, nil

View file

@ -1,68 +0,0 @@
package charger
// Code generated by github.com/evcc-io/evcc/cmd/tools/decorate.go. DO NOT EDIT.
import (
"reflect"
"github.com/evcc-io/evcc/api"
)
func decorateEEBus(base *EEBus, meter func() (float64, error), phaseCurrents func() (float64, float64, float64, error), chargeRater func() (float64, error)) api.Charger {
caps := make(map[reflect.Type]any)
if meter != nil {
caps[reflect.TypeFor[api.Meter]()] = &decorateEEBusMeterImpl{meter: meter}
}
if phaseCurrents != nil {
caps[reflect.TypeFor[api.PhaseCurrents]()] = &decorateEEBusPhaseCurrentsImpl{phaseCurrents: phaseCurrents}
}
if chargeRater != nil {
caps[reflect.TypeFor[api.ChargeRater]()] = &decorateEEBusChargeRaterImpl{chargeRater: chargeRater}
}
if len(caps) == 0 {
return base
}
return &decorateEEBusCapable{EEBus: base, caps: caps}
}
type decorateEEBusCapable struct {
*EEBus
caps map[reflect.Type]any
}
func (d *decorateEEBusCapable) Capability(typ reflect.Type) (any, bool) {
c, ok := d.caps[typ]
if !ok && reflect.TypeOf(d).Implements(typ) {
return d, true
}
return c, ok
}
type decorateEEBusChargeRaterImpl struct {
chargeRater func() (float64, error)
}
func (impl *decorateEEBusChargeRaterImpl) ChargedEnergy() (float64, error) {
return impl.chargeRater()
}
type decorateEEBusMeterImpl struct {
meter func() (float64, error)
}
func (impl *decorateEEBusMeterImpl) CurrentPower() (float64, error) {
return impl.meter()
}
type decorateEEBusPhaseCurrentsImpl struct {
phaseCurrents func() (float64, float64, float64, error)
}
func (impl *decorateEEBusPhaseCurrentsImpl) Currents() (float64, float64, float64, error) {
return impl.phaseCurrents()
}

View file

@ -25,6 +25,7 @@ import (
"github.com/cenkalti/backoff/v4"
"github.com/evcc-io/evcc/api"
"github.com/evcc-io/evcc/api/implement"
"github.com/evcc-io/evcc/util"
"github.com/evcc-io/evcc/util/modbus"
"github.com/volkszaehler/mbmd/meters/rs485"
@ -34,6 +35,7 @@ import (
// Em2Go charger implementation
type Em2Go struct {
implement.Caps
log *util.Logger
conn *modbus.Connection
current uint16
@ -70,8 +72,6 @@ func init() {
registry.AddCtx("em2go-home", NewEm2GoFromConfig)
}
//go:generate go tool decorate -f decorateEm2Go -b *Em2Go -r api.Charger -t api.ChargerEx,api.PhaseSwitcher,api.PhaseGetter
// NewEm2GoFromConfig creates a Em2Go charger from generic config
func NewEm2GoFromConfig(ctx context.Context, other map[string]any) (api.Charger, error) {
cc := struct {
@ -104,6 +104,7 @@ func NewEm2Go(ctx context.Context, uri string, slaveID uint8) (api.Charger, erro
conn.Logger(log.TRACE)
wb := &Em2Go{
Caps: implement.New(),
log: log,
conn: conn,
current: 60,
@ -149,12 +150,6 @@ func (wb *Em2Go) heartbeat(ctx context.Context, interval time.Duration) {
// initialize performs common initialization for both Em2Go models
func (wb *Em2Go) initialize() (api.Charger, error) {
var (
maxCurrent func(float64) error
phases1p3p func(int) error
phasesG func() (int, error)
)
// test if workaround is needed (Home fw <1.3)
if err := wb.maxCurrentMillis(6.1); err != nil {
return nil, err
@ -169,15 +164,15 @@ func (wb *Em2Go) initialize() (api.Charger, error) {
if chargerCurrent == 6 {
wb.workaround = true
} else {
maxCurrent = wb.maxCurrentMillis
implement.Has(wb, implement.ChargerEx(wb.maxCurrentMillis))
}
if _, err := wb.conn.ReadHoldingRegisters(em2GoRegPhases, 1); err == nil {
phases1p3p = wb.phases1p3p
phasesG = wb.getPhases
implement.Has(wb, implement.PhaseSwitcher(wb.phases1p3p))
implement.Has(wb, implement.PhaseGetter(wb.getPhases))
}
return decorateEm2Go(wb, maxCurrent, phases1p3p, phasesG), nil
return wb, nil
}
// Status implements the api.Charger interface

View file

@ -1,68 +0,0 @@
package charger
// Code generated by github.com/evcc-io/evcc/cmd/tools/decorate.go. DO NOT EDIT.
import (
"reflect"
"github.com/evcc-io/evcc/api"
)
func decorateEm2Go(base *Em2Go, chargerEx func(float64) error, phaseSwitcher func(int) error, phaseGetter func() (int, error)) api.Charger {
caps := make(map[reflect.Type]any)
if chargerEx != nil {
caps[reflect.TypeFor[api.ChargerEx]()] = &decorateEm2GoChargerExImpl{chargerEx: chargerEx}
}
if phaseSwitcher != nil {
caps[reflect.TypeFor[api.PhaseSwitcher]()] = &decorateEm2GoPhaseSwitcherImpl{phaseSwitcher: phaseSwitcher}
}
if phaseGetter != nil {
caps[reflect.TypeFor[api.PhaseGetter]()] = &decorateEm2GoPhaseGetterImpl{phaseGetter: phaseGetter}
}
if len(caps) == 0 {
return base
}
return &decorateEm2GoCapable{Em2Go: base, caps: caps}
}
type decorateEm2GoCapable struct {
*Em2Go
caps map[reflect.Type]any
}
func (d *decorateEm2GoCapable) Capability(typ reflect.Type) (any, bool) {
c, ok := d.caps[typ]
if !ok && reflect.TypeOf(d).Implements(typ) {
return d, true
}
return c, ok
}
type decorateEm2GoChargerExImpl struct {
chargerEx func(float64) error
}
func (impl *decorateEm2GoChargerExImpl) MaxCurrentMillis(p0 float64) error {
return impl.chargerEx(p0)
}
type decorateEm2GoPhaseGetterImpl struct {
phaseGetter func() (int, error)
}
func (impl *decorateEm2GoPhaseGetterImpl) GetPhases() (int, error) {
return impl.phaseGetter()
}
type decorateEm2GoPhaseSwitcherImpl struct {
phaseSwitcher func(int) error
}
func (impl *decorateEm2GoPhaseSwitcherImpl) Phases1p3p(p0 int) error {
return impl.phaseSwitcher(p0)
}

View file

@ -23,6 +23,7 @@ import (
"fmt"
"github.com/evcc-io/evcc/api"
"github.com/evcc-io/evcc/api/implement"
"github.com/evcc-io/evcc/util"
"github.com/evcc-io/evcc/util/modbus"
)
@ -32,6 +33,7 @@ import (
// ETEK EKEPC2 charger implementation
type Etek struct {
implement.Caps
log *util.Logger
conn *modbus.Connection
}
@ -65,8 +67,6 @@ func init() {
registry.AddCtx("etek", NewEtekFromConfig)
}
//go:generate go tool decorate -f decorateEtek -b *Etek -r api.Charger -t api.Meter,api.MeterEnergy,api.PhaseVoltages
// NewEtekFromConfig creates an ETEK EKEPC2 charger from generic config
func NewEtekFromConfig(ctx context.Context, other map[string]any) (api.Charger, error) {
cc := modbus.Settings{
@ -84,23 +84,18 @@ func NewEtekFromConfig(ctx context.Context, other map[string]any) (api.Charger,
// Check if external meter is configured
// If register value is 65535 (0xffff), no external meter is configured
var (
currentPower func() (float64, error)
totalEnergy func() (float64, error)
voltages func() (float64, float64, float64, error)
)
// Check power register (94)
if b, err := wb.conn.ReadHoldingRegisters(etekRegMeterPowerAddr, 1); err == nil {
if binary.BigEndian.Uint16(b) != etekRegInvalidMeterAddr {
currentPower = wb.currentPower
implement.Has(wb, implement.Meter(wb.currentPower))
}
}
// Check energy register (95)
if b, err := wb.conn.ReadHoldingRegisters(etekRegMeterEnergyAddr, 1); err == nil {
if binary.BigEndian.Uint16(b) != etekRegInvalidMeterAddr {
totalEnergy = wb.totalEnergy
implement.Has(wb, implement.MeterEnergy(wb.totalEnergy))
}
}
@ -110,11 +105,11 @@ func NewEtekFromConfig(ctx context.Context, other map[string]any) (api.Charger,
l2 := binary.BigEndian.Uint16(b[2:4])
l3 := binary.BigEndian.Uint16(b[4:6])
if l1 != etekRegInvalidMeterAddr && l2 != etekRegInvalidMeterAddr && l3 != etekRegInvalidMeterAddr {
voltages = wb.voltages
implement.Has(wb, implement.PhaseVoltages(wb.voltages))
}
}
return decorateEtek(wb, currentPower, totalEnergy, voltages), nil
return wb, nil
}
// NewEtek creates an ETEK EKEPC2 charger
@ -128,6 +123,7 @@ func NewEtek(ctx context.Context, uri, device, comset string, baudrate int, prot
conn.Logger(log.TRACE)
wb := &Etek{
Caps: implement.New(),
log: log,
conn: conn,
}

View file

@ -1,68 +0,0 @@
package charger
// Code generated by github.com/evcc-io/evcc/cmd/tools/decorate.go. DO NOT EDIT.
import (
"reflect"
"github.com/evcc-io/evcc/api"
)
func decorateEtek(base *Etek, meter func() (float64, error), meterEnergy func() (float64, error), phaseVoltages func() (float64, float64, float64, error)) api.Charger {
caps := make(map[reflect.Type]any)
if meter != nil {
caps[reflect.TypeFor[api.Meter]()] = &decorateEtekMeterImpl{meter: meter}
}
if meterEnergy != nil {
caps[reflect.TypeFor[api.MeterEnergy]()] = &decorateEtekMeterEnergyImpl{meterEnergy: meterEnergy}
}
if phaseVoltages != nil {
caps[reflect.TypeFor[api.PhaseVoltages]()] = &decorateEtekPhaseVoltagesImpl{phaseVoltages: phaseVoltages}
}
if len(caps) == 0 {
return base
}
return &decorateEtekCapable{Etek: base, caps: caps}
}
type decorateEtekCapable struct {
*Etek
caps map[reflect.Type]any
}
func (d *decorateEtekCapable) Capability(typ reflect.Type) (any, bool) {
c, ok := d.caps[typ]
if !ok && reflect.TypeOf(d).Implements(typ) {
return d, true
}
return c, ok
}
type decorateEtekMeterImpl struct {
meter func() (float64, error)
}
func (impl *decorateEtekMeterImpl) CurrentPower() (float64, error) {
return impl.meter()
}
type decorateEtekMeterEnergyImpl struct {
meterEnergy func() (float64, error)
}
func (impl *decorateEtekMeterEnergyImpl) TotalEnergy() (float64, error) {
return impl.meterEnergy()
}
type decorateEtekPhaseVoltagesImpl struct {
phaseVoltages func() (float64, float64, float64, error)
}
func (impl *decorateEtekPhaseVoltagesImpl) Voltages() (float64, float64, float64, error) {
return impl.phaseVoltages()
}

View file

@ -25,6 +25,7 @@ import (
"time"
"github.com/evcc-io/evcc/api"
"github.com/evcc-io/evcc/api/implement"
"github.com/evcc-io/evcc/util"
"github.com/evcc-io/evcc/util/request"
"github.com/evcc-io/evcc/util/sponsor"
@ -33,6 +34,7 @@ import (
// EVECUBE charger implementation
type EVECUBE struct {
implement.Caps
*request.Helper
uri string
connector int
@ -46,8 +48,6 @@ func init() {
registry.Add("evecube", NewEVECUBEFromConfig)
}
//go:generate go tool decorate -f decorateEVECUBE -b *EVECUBE -r api.Charger -t api.PhaseSwitcher,api.Identifier
// EVECUBEUnitConfig is the /api/admin/unitconfig response
type EVECUBEUnitConfig struct {
ForcePhaseCharging int `json:"ForcePhaseCharging"`
@ -135,15 +135,12 @@ func NewEVECUBEFromConfig(other map[string]any) (api.Charger, error) {
// Phases1p3p and Identify APIs affect the entire charger, not individual connectors
// Only enable these APIs if the charger has a single connector
var phases1p3p func(int) error
var identify func() (string, error)
if config.NumberOfConnectors == 1 {
phases1p3p = wb.phases1p3p
identify = wb.identify
implement.Has(wb, implement.PhaseSwitcher(wb.phases1p3p))
implement.Has(wb, implement.Identifier(wb.identify))
}
return decorateEVECUBE(wb, phases1p3p, identify), nil
return wb, nil
}
// NewEVECUBE creates EVECUBE charger
@ -151,6 +148,7 @@ func NewEVECUBE(uri, user, password string, connector int, cache time.Duration)
log := util.NewLogger("evecube")
wb := &EVECUBE{
Caps: implement.New(),
Helper: request.NewHelper(log),
uri: strings.TrimRight(uri, "/"),
connector: connector,

View file

@ -1,56 +0,0 @@
package charger
// Code generated by github.com/evcc-io/evcc/cmd/tools/decorate.go. DO NOT EDIT.
import (
"reflect"
"github.com/evcc-io/evcc/api"
)
func decorateEVECUBE(base *EVECUBE, phaseSwitcher func(int) error, identifier func() (string, error)) api.Charger {
caps := make(map[reflect.Type]any)
if phaseSwitcher != nil {
caps[reflect.TypeFor[api.PhaseSwitcher]()] = &decorateEVECUBEPhaseSwitcherImpl{phaseSwitcher: phaseSwitcher}
}
if identifier != nil {
caps[reflect.TypeFor[api.Identifier]()] = &decorateEVECUBEIdentifierImpl{identifier: identifier}
}
if len(caps) == 0 {
return base
}
return &decorateEVECUBECapable{EVECUBE: base, caps: caps}
}
type decorateEVECUBECapable struct {
*EVECUBE
caps map[reflect.Type]any
}
func (d *decorateEVECUBECapable) Capability(typ reflect.Type) (any, bool) {
c, ok := d.caps[typ]
if !ok && reflect.TypeOf(d).Implements(typ) {
return d, true
}
return c, ok
}
type decorateEVECUBEIdentifierImpl struct {
identifier func() (string, error)
}
func (impl *decorateEVECUBEIdentifierImpl) Identify() (string, error) {
return impl.identifier()
}
type decorateEVECUBEPhaseSwitcherImpl struct {
phaseSwitcher func(int) error
}
func (impl *decorateEVECUBEPhaseSwitcherImpl) Phases1p3p(p0 int) error {
return impl.phaseSwitcher(p0)
}

View file

@ -7,6 +7,7 @@ import (
"time"
"github.com/evcc-io/evcc/api"
"github.com/evcc-io/evcc/api/implement"
"github.com/evcc-io/evcc/util"
"github.com/evcc-io/evcc/util/modbus"
"github.com/volkszaehler/mbmd/encoding"
@ -14,6 +15,7 @@ import (
// EvseDIN charger implementation
type EvseDIN struct {
implement.Caps
conn *modbus.Connection
current uint16
}
@ -46,8 +48,6 @@ func NewEvseDINFromConfig(ctx context.Context, other map[string]any) (api.Charge
return NewEvseDIN(ctx, cc.URI, cc.Device, cc.Comset, cc.Baudrate, cc.Protocol(), cc.ID)
}
//go:generate go tool decorate -f decorateEvseDIN -b *EvseDIN -r api.Charger -t api.ChargerEx
// NewEvseDIN creates EVSE DIN charger
func NewEvseDIN(ctx context.Context, uri, device, comset string, baudrate int, proto modbus.Protocol, slaveID uint8) (api.Charger, error) {
log := util.NewLogger("evse")
@ -61,12 +61,11 @@ func NewEvseDIN(ctx context.Context, uri, device, comset string, baudrate int, p
conn.Delay(200 * time.Millisecond)
evse := &EvseDIN{
Caps: implement.New(),
conn: conn,
current: 6, // assume min current
}
var maxCurrentMillis func(float64) error
// check firmware
bFirmware, err := evse.conn.ReadHoldingRegisters(evseRegFirmware, 1)
if err != nil {
@ -94,7 +93,7 @@ func NewEvseDIN(ctx context.Context, uri, device, comset string, baudrate int, p
}
if config&0x0080 != 0 {
maxCurrentMillis = evse.maxCurrentMillis
implement.Has(evse, implement.ChargerEx(evse.maxCurrentMillis))
evse.current = 600 // assume min current
}
}
@ -107,7 +106,7 @@ func NewEvseDIN(ctx context.Context, uri, device, comset string, baudrate int, p
evse.current = current
}
return decorateEvseDIN(evse, maxCurrentMillis), nil
return evse, nil
}
// Status implements the api.Charger interface

View file

@ -1,44 +0,0 @@
package charger
// Code generated by github.com/evcc-io/evcc/cmd/tools/decorate.go. DO NOT EDIT.
import (
"reflect"
"github.com/evcc-io/evcc/api"
)
func decorateEvseDIN(base *EvseDIN, chargerEx func(float64) error) api.Charger {
caps := make(map[reflect.Type]any)
if chargerEx != nil {
caps[reflect.TypeFor[api.ChargerEx]()] = &decorateEvseDINChargerExImpl{chargerEx: chargerEx}
}
if len(caps) == 0 {
return base
}
return &decorateEvseDINCapable{EvseDIN: base, caps: caps}
}
type decorateEvseDINCapable struct {
*EvseDIN
caps map[reflect.Type]any
}
func (d *decorateEvseDINCapable) Capability(typ reflect.Type) (any, bool) {
c, ok := d.caps[typ]
if !ok && reflect.TypeOf(d).Implements(typ) {
return d, true
}
return c, ok
}
type decorateEvseDINChargerExImpl struct {
chargerEx func(float64) error
}
func (impl *decorateEvseDINChargerExImpl) MaxCurrentMillis(p0 float64) error {
return impl.chargerEx(p0)
}

View file

@ -7,6 +7,7 @@ import (
"time"
"github.com/evcc-io/evcc/api"
"github.com/evcc-io/evcc/api/implement"
"github.com/evcc-io/evcc/charger/evse"
"github.com/evcc-io/evcc/util"
"github.com/evcc-io/evcc/util/request"
@ -15,6 +16,7 @@ import (
// EVSEWifi charger implementation
type EVSEWifi struct {
*request.Helper
implement.Caps
uri string
alwaysActive bool
current int64 // current will always be the physical value sent to the API
@ -27,8 +29,6 @@ func init() {
registry.Add("evsewifi", NewEVSEWifiFromConfig)
}
//go:generate go tool decorate -f decorateEVSE -b *EVSEWifi -r api.Charger -t api.Meter,api.MeterEnergy,api.PhaseCurrents,api.PhaseVoltages,api.ChargerEx,api.Identifier
// NewEVSEWifiFromConfig creates a EVSEWifi charger from generic config
func NewEVSEWifiFromConfig(other map[string]any) (api.Charger, error) {
cc := struct {
@ -71,42 +71,32 @@ func NewEVSEWifiFromConfig(other map[string]any) (api.Charger, error) {
wb.hires = true
}
// decorate meter
var (
power, energy func() (float64, error)
currents, voltages func() (float64, float64, float64, error)
)
if cc.Meter.Power {
power = wb.currentPower
implement.Has(wb, implement.Meter(wb.currentPower))
}
if cc.Meter.Energy {
energy = wb.totalEnergy
implement.Has(wb, implement.MeterEnergy(wb.totalEnergy))
}
if cc.Meter.Currents {
currents = wb.currents
implement.Has(wb, implement.PhaseCurrents(wb.currents))
}
if cc.Meter.Voltages {
voltages = wb.voltages
implement.Has(wb, implement.PhaseVoltages(wb.voltages))
}
// decorate Charger with MaxCurrentMillis
var maxCurrentMillis func(float64) error
if wb.hires {
maxCurrentMillis = wb.maxCurrentMillis
implement.Has(wb, implement.ChargerEx(wb.maxCurrentMillis))
wb.current = 100 * wb.current
}
// decorate Charger with Identifier
var identify func() (string, error)
if params.RFIDUID != nil {
identify = wb.identify
implement.Has(wb, implement.Identifier(wb.identify))
}
return decorateEVSE(wb, power, energy, currents, voltages, maxCurrentMillis, identify), nil
return wb, nil
}
// NewEVSEWifi creates EVSEWifi charger
@ -115,6 +105,7 @@ func NewEVSEWifi(uri string, cache time.Duration) (*EVSEWifi, error) {
wb := &EVSEWifi{
Helper: request.NewHelper(log),
Caps: implement.New(),
uri: strings.TrimRight(uri, "/"),
current: 6, // 6A defined value
}

View file

@ -1,104 +0,0 @@
package charger
// Code generated by github.com/evcc-io/evcc/cmd/tools/decorate.go. DO NOT EDIT.
import (
"reflect"
"github.com/evcc-io/evcc/api"
)
func decorateEVSE(base *EVSEWifi, meter func() (float64, error), meterEnergy func() (float64, error), phaseCurrents func() (float64, float64, float64, error), phaseVoltages func() (float64, float64, float64, error), chargerEx func(float64) error, identifier func() (string, error)) api.Charger {
caps := make(map[reflect.Type]any)
if meter != nil {
caps[reflect.TypeFor[api.Meter]()] = &decorateEVSEMeterImpl{meter: meter}
}
if meterEnergy != nil {
caps[reflect.TypeFor[api.MeterEnergy]()] = &decorateEVSEMeterEnergyImpl{meterEnergy: meterEnergy}
}
if phaseCurrents != nil {
caps[reflect.TypeFor[api.PhaseCurrents]()] = &decorateEVSEPhaseCurrentsImpl{phaseCurrents: phaseCurrents}
}
if phaseVoltages != nil {
caps[reflect.TypeFor[api.PhaseVoltages]()] = &decorateEVSEPhaseVoltagesImpl{phaseVoltages: phaseVoltages}
}
if chargerEx != nil {
caps[reflect.TypeFor[api.ChargerEx]()] = &decorateEVSEChargerExImpl{chargerEx: chargerEx}
}
if identifier != nil {
caps[reflect.TypeFor[api.Identifier]()] = &decorateEVSEIdentifierImpl{identifier: identifier}
}
if len(caps) == 0 {
return base
}
return &decorateEVSECapable{EVSEWifi: base, caps: caps}
}
type decorateEVSECapable struct {
*EVSEWifi
caps map[reflect.Type]any
}
func (d *decorateEVSECapable) Capability(typ reflect.Type) (any, bool) {
c, ok := d.caps[typ]
if !ok && reflect.TypeOf(d).Implements(typ) {
return d, true
}
return c, ok
}
type decorateEVSEChargerExImpl struct {
chargerEx func(float64) error
}
func (impl *decorateEVSEChargerExImpl) MaxCurrentMillis(p0 float64) error {
return impl.chargerEx(p0)
}
type decorateEVSEIdentifierImpl struct {
identifier func() (string, error)
}
func (impl *decorateEVSEIdentifierImpl) Identify() (string, error) {
return impl.identifier()
}
type decorateEVSEMeterImpl struct {
meter func() (float64, error)
}
func (impl *decorateEVSEMeterImpl) CurrentPower() (float64, error) {
return impl.meter()
}
type decorateEVSEMeterEnergyImpl struct {
meterEnergy func() (float64, error)
}
func (impl *decorateEVSEMeterEnergyImpl) TotalEnergy() (float64, error) {
return impl.meterEnergy()
}
type decorateEVSEPhaseCurrentsImpl struct {
phaseCurrents func() (float64, float64, float64, error)
}
func (impl *decorateEVSEPhaseCurrentsImpl) Currents() (float64, float64, float64, error) {
return impl.phaseCurrents()
}
type decorateEVSEPhaseVoltagesImpl struct {
phaseVoltages func() (float64, float64, float64, error)
}
func (impl *decorateEVSEPhaseVoltagesImpl) Voltages() (float64, float64, float64, error) {
return impl.phaseVoltages()
}

View file

@ -8,6 +8,7 @@ import (
"github.com/enbility/spine-go/model"
"github.com/evcc-io/evcc/api"
"github.com/evcc-io/evcc/api/implement"
"github.com/evcc-io/evcc/charger/ghostone"
"github.com/evcc-io/evcc/util"
"github.com/evcc-io/evcc/util/request"
@ -49,8 +50,6 @@ func NewGhostEEBusFromConfig(ctx context.Context, other map[string]any) (api.Cha
return NewGhostEEBus(ctx, cc.Ski, cc.Ip, cc.User, cc.Password, cc.Meter, hasChargedEnergy)
}
//go:generate go tool decorate -f decorateGhostEEBus -b *GhostEEBus -r api.Charger -t api.Meter,api.PhaseCurrents,api.ChargeRater,api.PhaseSwitcher,api.PhaseGetter
// NewGhostEEBus creates a GhostEEBus charger combining EEBus with Ghost REST API
func NewGhostEEBus(ctx context.Context, ski, ip, user, password string, hasMeter, hasChargedEnergy bool) (api.Charger, error) {
eb, err := newEEBus(ctx, ski, ip)
@ -69,9 +68,6 @@ func NewGhostEEBus(ctx context.Context, ski, ip, user, password string, hasMeter
}
// REST API features require IP and credentials
var phasesS func(int) error
var phasesG func() (int, error)
if ip != "" && user != "" && password != "" {
ts, err := ghostone.TokenSource(ctx, log, wb.uri, user, password)
if err != nil {
@ -97,25 +93,21 @@ func NewGhostEEBus(ctx context.Context, ski, ip, user, password string, hasMeter
// always wire up phase switching and RFID - the wallbox will reject
// operations at runtime if the feature is disabled or not possible
phasesS = wb.phases1p3p
phasesG = wb.getPhases
implement.Has(wb, implement.PhaseSwitcher(wb.phases1p3p))
implement.Has(wb, implement.PhaseGetter(wb.getPhases))
wb.hasRFID = true
}
// EEBus meter capabilities
var meter func() (float64, error)
var phaseCurrents func() (float64, float64, float64, error)
var chargeRater func() (float64, error)
if hasMeter {
meter = eb.currentPower
phaseCurrents = eb.currents
implement.Has(wb, implement.Meter(eb.currentPower))
implement.Has(wb, implement.PhaseCurrents(eb.currents))
if hasChargedEnergy {
chargeRater = eb.chargedEnergy
implement.Has(wb, implement.ChargeRater(eb.chargedEnergy))
}
}
return decorateGhostEEBus(wb, meter, phaseCurrents, chargeRater, phasesS, phasesG), nil
return wb, nil
}
var _ api.Identifier = (*GhostEEBus)(nil)

View file

@ -1,92 +0,0 @@
package charger
// Code generated by github.com/evcc-io/evcc/cmd/tools/decorate.go. DO NOT EDIT.
import (
"reflect"
"github.com/evcc-io/evcc/api"
)
func decorateGhostEEBus(base *GhostEEBus, meter func() (float64, error), phaseCurrents func() (float64, float64, float64, error), chargeRater func() (float64, error), phaseSwitcher func(int) error, phaseGetter func() (int, error)) api.Charger {
caps := make(map[reflect.Type]any)
if meter != nil {
caps[reflect.TypeFor[api.Meter]()] = &decorateGhostEEBusMeterImpl{meter: meter}
}
if phaseCurrents != nil {
caps[reflect.TypeFor[api.PhaseCurrents]()] = &decorateGhostEEBusPhaseCurrentsImpl{phaseCurrents: phaseCurrents}
}
if chargeRater != nil {
caps[reflect.TypeFor[api.ChargeRater]()] = &decorateGhostEEBusChargeRaterImpl{chargeRater: chargeRater}
}
if phaseSwitcher != nil {
caps[reflect.TypeFor[api.PhaseSwitcher]()] = &decorateGhostEEBusPhaseSwitcherImpl{phaseSwitcher: phaseSwitcher}
}
if phaseGetter != nil {
caps[reflect.TypeFor[api.PhaseGetter]()] = &decorateGhostEEBusPhaseGetterImpl{phaseGetter: phaseGetter}
}
if len(caps) == 0 {
return base
}
return &decorateGhostEEBusCapable{GhostEEBus: base, caps: caps}
}
type decorateGhostEEBusCapable struct {
*GhostEEBus
caps map[reflect.Type]any
}
func (d *decorateGhostEEBusCapable) Capability(typ reflect.Type) (any, bool) {
c, ok := d.caps[typ]
if !ok && reflect.TypeOf(d).Implements(typ) {
return d, true
}
return c, ok
}
type decorateGhostEEBusChargeRaterImpl struct {
chargeRater func() (float64, error)
}
func (impl *decorateGhostEEBusChargeRaterImpl) ChargedEnergy() (float64, error) {
return impl.chargeRater()
}
type decorateGhostEEBusMeterImpl struct {
meter func() (float64, error)
}
func (impl *decorateGhostEEBusMeterImpl) CurrentPower() (float64, error) {
return impl.meter()
}
type decorateGhostEEBusPhaseCurrentsImpl struct {
phaseCurrents func() (float64, float64, float64, error)
}
func (impl *decorateGhostEEBusPhaseCurrentsImpl) Currents() (float64, float64, float64, error) {
return impl.phaseCurrents()
}
type decorateGhostEEBusPhaseGetterImpl struct {
phaseGetter func() (int, error)
}
func (impl *decorateGhostEEBusPhaseGetterImpl) GetPhases() (int, error) {
return impl.phaseGetter()
}
type decorateGhostEEBusPhaseSwitcherImpl struct {
phaseSwitcher func(int) error
}
func (impl *decorateGhostEEBusPhaseSwitcherImpl) Phases1p3p(p0 int) error {
return impl.phaseSwitcher(p0)
}

View file

@ -12,6 +12,7 @@ import (
spinemocks "github.com/enbility/spine-go/mocks"
"github.com/enbility/spine-go/model"
"github.com/evcc-io/evcc/api"
"github.com/evcc-io/evcc/api/implement"
"github.com/evcc-io/evcc/charger/ghostone"
"github.com/evcc-io/evcc/server/eebus"
"github.com/evcc-io/evcc/util"
@ -26,6 +27,7 @@ func newTestGhostEEBusREST(t *testing.T) *GhostEEBus {
t.Helper()
return &GhostEEBus{
EEBus: &EEBus{
Caps: implement.New(),
log: util.NewLogger("test"),
cem: &eebus.CustomerEnergyManagement{},
},
@ -42,6 +44,7 @@ func newTestGhostEEBusWithEEBus(t *testing.T) (*GhostEEBus, *mocks.CemEVCCInterf
evEntity := spinemocks.NewEntityRemoteInterface(t)
eb := &EEBus{
Caps: implement.New(),
cem: &eebus.CustomerEnergyManagement{
EvCC: evccMock,
},
@ -285,13 +288,14 @@ func TestGhostEEBus_Decorator(t *testing.T) {
wb := newTestGhostEEBusREST(t)
// with phase switching
decorated := decorateGhostEEBus(wb, nil, nil, nil, wb.phases1p3p, wb.getPhases)
assert.True(t, api.HasCap[api.PhaseSwitcher](decorated), "expected PhaseSwitcher")
assert.True(t, api.HasCap[api.PhaseGetter](decorated), "expected PhaseGetter")
implement.May(wb, implement.PhaseSwitcher(wb.phases1p3p))
implement.May(wb, implement.PhaseGetter(wb.getPhases))
assert.True(t, api.HasCap[api.PhaseSwitcher](wb), "expected PhaseSwitcher")
assert.True(t, api.HasCap[api.PhaseGetter](wb), "expected PhaseGetter")
// without phase switching
decorated = decorateGhostEEBus(wb, nil, nil, nil, nil, nil)
assert.False(t, api.HasCap[api.PhaseSwitcher](decorated), "unexpected PhaseSwitcher")
// without phase switching — capabilities not registered, so expect false
wb2 := newTestGhostEEBusREST(t)
assert.False(t, api.HasCap[api.PhaseSwitcher](wb2), "unexpected PhaseSwitcher")
}
func TestGhostEEBus_Config(t *testing.T) {

View file

@ -23,6 +23,7 @@ import (
"time"
"github.com/evcc-io/evcc/api"
"github.com/evcc-io/evcc/api/implement"
goe "github.com/evcc-io/evcc/charger/go-e"
"github.com/evcc-io/evcc/util"
"github.com/evcc-io/evcc/util/sponsor"
@ -34,6 +35,7 @@ import (
// GoE charger implementation
type GoE struct {
implement.Caps
api goe.API
}
@ -46,8 +48,6 @@ func init() {
})
}
//go:generate go tool decorate -f decorateGoE -b *GoE -r api.Charger -t api.ChargeRater,api.PhaseSwitcher
// newGoEFromConfig creates a go-e charger from generic config
func newGoEFromConfig(v2 bool, other map[string]any) (api.Charger, error) {
cc := struct {
@ -71,22 +71,22 @@ func newGoEFromConfig(v2 bool, other map[string]any) (api.Charger, error) {
return nil, err
}
var chargedEnergy func() (float64, error)
if v2 {
chargedEnergy = c.chargedEnergy
implement.Has(c, implement.ChargeRater(c.chargedEnergy))
}
var phases1p3p func(int) error
if c.api.IsV2() {
phases1p3p = c.phases1p3p
implement.Has(c, implement.PhaseSwitcher(c.phases1p3p))
}
return decorateGoE(c, chargedEnergy, phases1p3p), nil
return c, nil
}
// NewGoE creates GoE charger
func NewGoE(uri, token string, cache time.Duration) (*GoE, error) {
c := &GoE{}
c := &GoE{
Caps: implement.New(),
}
log := util.NewLogger("go-e").Redact(token)

View file

@ -1,56 +0,0 @@
package charger
// Code generated by github.com/evcc-io/evcc/cmd/tools/decorate.go. DO NOT EDIT.
import (
"reflect"
"github.com/evcc-io/evcc/api"
)
func decorateGoE(base *GoE, chargeRater func() (float64, error), phaseSwitcher func(int) error) api.Charger {
caps := make(map[reflect.Type]any)
if chargeRater != nil {
caps[reflect.TypeFor[api.ChargeRater]()] = &decorateGoEChargeRaterImpl{chargeRater: chargeRater}
}
if phaseSwitcher != nil {
caps[reflect.TypeFor[api.PhaseSwitcher]()] = &decorateGoEPhaseSwitcherImpl{phaseSwitcher: phaseSwitcher}
}
if len(caps) == 0 {
return base
}
return &decorateGoECapable{GoE: base, caps: caps}
}
type decorateGoECapable struct {
*GoE
caps map[reflect.Type]any
}
func (d *decorateGoECapable) Capability(typ reflect.Type) (any, bool) {
c, ok := d.caps[typ]
if !ok && reflect.TypeOf(d).Implements(typ) {
return d, true
}
return c, ok
}
type decorateGoEChargeRaterImpl struct {
chargeRater func() (float64, error)
}
func (impl *decorateGoEChargeRaterImpl) ChargedEnergy() (float64, error) {
return impl.chargeRater()
}
type decorateGoEPhaseSwitcherImpl struct {
phaseSwitcher func(int) error
}
func (impl *decorateGoEPhaseSwitcherImpl) Phases1p3p(p0 int) error {
return impl.phaseSwitcher(p0)
}

View file

@ -28,6 +28,7 @@ import (
"github.com/cenkalti/backoff/v4"
"github.com/evcc-io/evcc/api"
"github.com/evcc-io/evcc/api/implement"
"github.com/evcc-io/evcc/charger/echarge"
"github.com/evcc-io/evcc/charger/echarge/salia"
"github.com/evcc-io/evcc/util"
@ -42,6 +43,7 @@ import (
// Salia charger implementation
type Salia struct {
*request.Helper
implement.Caps
log *util.Logger
uri string
current int64
@ -53,8 +55,6 @@ func init() {
registry.AddCtx("hardybarth-salia", NewSaliaFromConfig)
}
//go:generate go tool decorate -f decorateSalia -b *Salia -r api.Charger -t api.Meter,api.MeterEnergy,api.PhaseCurrents,api.PhaseSwitcher,api.PhaseGetter
// NewSaliaFromConfig creates a Salia cPH2 charger from generic config
func NewSaliaFromConfig(ctx context.Context, other map[string]any) (api.Charger, error) {
cc := struct {
@ -83,6 +83,7 @@ func NewSalia(ctx context.Context, uri, user, password string, cache time.Durati
wb := &Salia{
log: log,
Helper: request.NewHelper(log),
Caps: implement.New(),
uri: util.DefaultScheme(uri, "http"),
current: 6,
}
@ -138,26 +139,18 @@ func NewSalia(ctx context.Context, uri, user, password string, cache time.Durati
wb.pause(false)
var (
currentPower func() (float64, error)
totalEnergy func() (float64, error)
currents func() (float64, float64, float64, error)
phasesG func() (int, error)
phasesS func(int) error
)
if res.Secc.Port0.Metering.Meter.Available > 0 {
currentPower = wb.currentPower
totalEnergy = wb.totalEnergy
currents = wb.currents
implement.Has(wb, implement.Meter(wb.currentPower))
implement.Has(wb, implement.MeterEnergy(wb.totalEnergy))
implement.Has(wb, implement.PhaseCurrents(wb.currents))
}
if res.Secc.Port0.Salia.PhaseSwitching.Actual > 0 {
phasesG = wb.getPhases
phasesS = wb.phases1p3p
implement.Has(wb, implement.PhaseSwitcher(wb.phases1p3p))
implement.Has(wb, implement.PhaseGetter(wb.getPhases))
}
return decorateSalia(wb, currentPower, totalEnergy, currents, phasesS, phasesG), nil
return wb, nil
}
func (wb *Salia) heartbeat(ctx context.Context) {

View file

@ -1,92 +0,0 @@
package charger
// Code generated by github.com/evcc-io/evcc/cmd/tools/decorate.go. DO NOT EDIT.
import (
"reflect"
"github.com/evcc-io/evcc/api"
)
func decorateSalia(base *Salia, meter func() (float64, error), meterEnergy func() (float64, error), phaseCurrents func() (float64, float64, float64, error), phaseSwitcher func(int) error, phaseGetter func() (int, error)) api.Charger {
caps := make(map[reflect.Type]any)
if meter != nil {
caps[reflect.TypeFor[api.Meter]()] = &decorateSaliaMeterImpl{meter: meter}
}
if meterEnergy != nil {
caps[reflect.TypeFor[api.MeterEnergy]()] = &decorateSaliaMeterEnergyImpl{meterEnergy: meterEnergy}
}
if phaseCurrents != nil {
caps[reflect.TypeFor[api.PhaseCurrents]()] = &decorateSaliaPhaseCurrentsImpl{phaseCurrents: phaseCurrents}
}
if phaseSwitcher != nil {
caps[reflect.TypeFor[api.PhaseSwitcher]()] = &decorateSaliaPhaseSwitcherImpl{phaseSwitcher: phaseSwitcher}
}
if phaseGetter != nil {
caps[reflect.TypeFor[api.PhaseGetter]()] = &decorateSaliaPhaseGetterImpl{phaseGetter: phaseGetter}
}
if len(caps) == 0 {
return base
}
return &decorateSaliaCapable{Salia: base, caps: caps}
}
type decorateSaliaCapable struct {
*Salia
caps map[reflect.Type]any
}
func (d *decorateSaliaCapable) Capability(typ reflect.Type) (any, bool) {
c, ok := d.caps[typ]
if !ok && reflect.TypeOf(d).Implements(typ) {
return d, true
}
return c, ok
}
type decorateSaliaMeterImpl struct {
meter func() (float64, error)
}
func (impl *decorateSaliaMeterImpl) CurrentPower() (float64, error) {
return impl.meter()
}
type decorateSaliaMeterEnergyImpl struct {
meterEnergy func() (float64, error)
}
func (impl *decorateSaliaMeterEnergyImpl) TotalEnergy() (float64, error) {
return impl.meterEnergy()
}
type decorateSaliaPhaseCurrentsImpl struct {
phaseCurrents func() (float64, float64, float64, error)
}
func (impl *decorateSaliaPhaseCurrentsImpl) Currents() (float64, float64, float64, error) {
return impl.phaseCurrents()
}
type decorateSaliaPhaseGetterImpl struct {
phaseGetter func() (int, error)
}
func (impl *decorateSaliaPhaseGetterImpl) GetPhases() (int, error) {
return impl.phaseGetter()
}
type decorateSaliaPhaseSwitcherImpl struct {
phaseSwitcher func(int) error
}
func (impl *decorateSaliaPhaseSwitcherImpl) Phases1p3p(p0 int) error {
return impl.phaseSwitcher(p0)
}

View file

@ -21,6 +21,7 @@ import (
"context"
"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"
@ -30,6 +31,7 @@ import (
// Heatpump charger implementation
type Heatpump struct {
*embed
implement.Caps
lp loadpoint.API
power int64
maxPowerG func() (int64, error)
@ -40,8 +42,6 @@ func init() {
registry.AddCtx("heatpump", NewHeatpumpFromConfig)
}
//go:generate go tool decorate -f decorateHeatpump -b *Heatpump -r api.Charger -t api.Meter,api.MeterEnergy,api.Battery,api.SocLimiter
// NewHeatpumpFromConfig creates heatpump configurable charger from generic config
func NewHeatpumpFromConfig(ctx context.Context, other map[string]any) (api.Charger, error) {
cc := struct {
@ -84,19 +84,24 @@ func NewHeatpumpFromConfig(ctx context.Context, other map[string]any) (api.Charg
if err != nil {
return nil, err
}
implement.May(res, implement.Meter(powerG))
implement.May(res, implement.MeterEnergy(energyG))
tempG, limitTempG, err := cc.Temperature.Configure(ctx)
if err != nil {
return nil, err
}
implement.May(res, implement.Battery(tempG))
implement.May(res, implement.SocLimiter(limitTempG))
return decorateHeatpump(res, powerG, energyG, tempG, limitTempG), nil
return res, nil
}
// NewHeatpump creates heatpump charger
func NewHeatpump(ctx context.Context, embed *embed, maxPowerS func(int64) error, maxPowerG func() (int64, error)) (*Heatpump, error) {
res := &Heatpump{
embed: embed,
Caps: implement.New(),
maxPowerG: maxPowerG,
maxPowerS: maxPowerS,
}

View file

@ -1,80 +0,0 @@
package charger
// Code generated by github.com/evcc-io/evcc/cmd/tools/decorate.go. DO NOT EDIT.
import (
"reflect"
"github.com/evcc-io/evcc/api"
)
func decorateHeatpump(base *Heatpump, meter func() (float64, error), meterEnergy func() (float64, error), battery func() (float64, error), socLimiter func() (int64, error)) api.Charger {
caps := make(map[reflect.Type]any)
if meter != nil {
caps[reflect.TypeFor[api.Meter]()] = &decorateHeatpumpMeterImpl{meter: meter}
}
if meterEnergy != nil {
caps[reflect.TypeFor[api.MeterEnergy]()] = &decorateHeatpumpMeterEnergyImpl{meterEnergy: meterEnergy}
}
if battery != nil {
caps[reflect.TypeFor[api.Battery]()] = &decorateHeatpumpBatteryImpl{battery: battery}
}
if socLimiter != nil {
caps[reflect.TypeFor[api.SocLimiter]()] = &decorateHeatpumpSocLimiterImpl{socLimiter: socLimiter}
}
if len(caps) == 0 {
return base
}
return &decorateHeatpumpCapable{Heatpump: base, caps: caps}
}
type decorateHeatpumpCapable struct {
*Heatpump
caps map[reflect.Type]any
}
func (d *decorateHeatpumpCapable) Capability(typ reflect.Type) (any, bool) {
c, ok := d.caps[typ]
if !ok && reflect.TypeOf(d).Implements(typ) {
return d, true
}
return c, ok
}
type decorateHeatpumpBatteryImpl struct {
battery func() (float64, error)
}
func (impl *decorateHeatpumpBatteryImpl) Soc() (float64, error) {
return impl.battery()
}
type decorateHeatpumpMeterImpl struct {
meter func() (float64, error)
}
func (impl *decorateHeatpumpMeterImpl) CurrentPower() (float64, error) {
return impl.meter()
}
type decorateHeatpumpMeterEnergyImpl struct {
meterEnergy func() (float64, error)
}
func (impl *decorateHeatpumpMeterEnergyImpl) TotalEnergy() (float64, error) {
return impl.meterEnergy()
}
type decorateHeatpumpSocLimiterImpl struct {
socLimiter func() (int64, error)
}
func (impl *decorateHeatpumpSocLimiterImpl) GetLimitSoc() (int64, error) {
return impl.socLimiter()
}

View file

@ -1,20 +1,19 @@
package charger
//go:generate go tool decorate -f decorateHomeAssistant -b *HomeAssistant -r api.Charger -t api.Meter,api.MeterEnergy,api.PhaseCurrents,api.PhaseVoltages,api.PhaseSwitcher,api.PhaseGetter
// -t api.CurrentGetter
import (
"errors"
"fmt"
"strconv"
"github.com/evcc-io/evcc/api"
"github.com/evcc-io/evcc/api/implement"
"github.com/evcc-io/evcc/util"
"github.com/evcc-io/evcc/util/homeassistant"
)
// HomeAssistant charger implementation
type HomeAssistant struct {
implement.Caps
conn *homeassistant.Connection
status string
enabled string
@ -68,6 +67,7 @@ func NewHomeAssistantFromConfig(other map[string]any) (api.Charger, error) {
}
c := &HomeAssistant{
Caps: implement.New(),
conn: conn,
status: cc.Status,
enabled: cc.Enabled,
@ -75,49 +75,43 @@ func NewHomeAssistantFromConfig(other map[string]any) (api.Charger, error) {
maxcurrent: cc.MaxCurrent,
}
// decorators for optional interfaces
var power, energy func() (float64, error)
var currents, voltages func() (float64, float64, float64, error)
var phases1p3p func(int) error
var phasesG func() (int, error)
if cc.Power != "" {
power = func() (float64, error) { return conn.GetFloatState(cc.Power) }
implement.Has(c, implement.Meter(func() (float64, error) { return conn.GetFloatState(cc.Power) }))
}
if cc.Energy != "" {
energy = func() (float64, error) { return conn.GetFloatState(cc.Energy) }
implement.Has(c, implement.MeterEnergy(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) }
implement.Has(c, implement.PhaseCurrents(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) }
implement.Has(c, implement.PhaseVoltages(func() (float64, float64, float64, error) { return conn.GetPhaseFloatStates(phases) }))
} else if err != nil {
return nil, fmt.Errorf("voltages: %w", err)
}
// phase switching (optional)
if cc.Phases != "" {
phases1p3p = func(phases int) error {
implement.Has(c, implement.PhaseSwitcher(func(phases int) error {
return conn.CallSelectService(cc.Phases, strconv.Itoa(phases))
}
}))
phasesG = func() (int, error) {
implement.Has(c, implement.PhaseGetter(func() (int, error) {
val, err := conn.GetIntState(cc.Phases)
if err != nil {
return 0, err
}
return int(val), nil
}
}))
}
return decorateHomeAssistant(c, power, energy, currents, voltages, phases1p3p, phasesG), nil
return c, nil
}
var _ api.Charger = (*HomeAssistant)(nil)

View file

@ -1,104 +0,0 @@
package charger
// Code generated by github.com/evcc-io/evcc/cmd/tools/decorate.go. DO NOT EDIT.
import (
"reflect"
"github.com/evcc-io/evcc/api"
)
func decorateHomeAssistant(base *HomeAssistant, meter func() (float64, error), meterEnergy func() (float64, error), phaseCurrents func() (float64, float64, float64, error), phaseVoltages func() (float64, float64, float64, error), phaseSwitcher func(int) error, phaseGetter func() (int, error)) api.Charger {
caps := make(map[reflect.Type]any)
if meter != nil {
caps[reflect.TypeFor[api.Meter]()] = &decorateHomeAssistantMeterImpl{meter: meter}
}
if meterEnergy != nil {
caps[reflect.TypeFor[api.MeterEnergy]()] = &decorateHomeAssistantMeterEnergyImpl{meterEnergy: meterEnergy}
}
if phaseCurrents != nil {
caps[reflect.TypeFor[api.PhaseCurrents]()] = &decorateHomeAssistantPhaseCurrentsImpl{phaseCurrents: phaseCurrents}
}
if phaseVoltages != nil {
caps[reflect.TypeFor[api.PhaseVoltages]()] = &decorateHomeAssistantPhaseVoltagesImpl{phaseVoltages: phaseVoltages}
}
if phaseSwitcher != nil {
caps[reflect.TypeFor[api.PhaseSwitcher]()] = &decorateHomeAssistantPhaseSwitcherImpl{phaseSwitcher: phaseSwitcher}
}
if phaseGetter != nil {
caps[reflect.TypeFor[api.PhaseGetter]()] = &decorateHomeAssistantPhaseGetterImpl{phaseGetter: phaseGetter}
}
if len(caps) == 0 {
return base
}
return &decorateHomeAssistantCapable{HomeAssistant: base, caps: caps}
}
type decorateHomeAssistantCapable struct {
*HomeAssistant
caps map[reflect.Type]any
}
func (d *decorateHomeAssistantCapable) Capability(typ reflect.Type) (any, bool) {
c, ok := d.caps[typ]
if !ok && reflect.TypeOf(d).Implements(typ) {
return d, true
}
return c, ok
}
type decorateHomeAssistantMeterImpl struct {
meter func() (float64, error)
}
func (impl *decorateHomeAssistantMeterImpl) CurrentPower() (float64, error) {
return impl.meter()
}
type decorateHomeAssistantMeterEnergyImpl struct {
meterEnergy func() (float64, error)
}
func (impl *decorateHomeAssistantMeterEnergyImpl) TotalEnergy() (float64, error) {
return impl.meterEnergy()
}
type decorateHomeAssistantPhaseCurrentsImpl struct {
phaseCurrents func() (float64, float64, float64, error)
}
func (impl *decorateHomeAssistantPhaseCurrentsImpl) Currents() (float64, float64, float64, error) {
return impl.phaseCurrents()
}
type decorateHomeAssistantPhaseGetterImpl struct {
phaseGetter func() (int, error)
}
func (impl *decorateHomeAssistantPhaseGetterImpl) GetPhases() (int, error) {
return impl.phaseGetter()
}
type decorateHomeAssistantPhaseSwitcherImpl struct {
phaseSwitcher func(int) error
}
func (impl *decorateHomeAssistantPhaseSwitcherImpl) Phases1p3p(p0 int) error {
return impl.phaseSwitcher(p0)
}
type decorateHomeAssistantPhaseVoltagesImpl struct {
phaseVoltages func() (float64, float64, float64, error)
}
func (impl *decorateHomeAssistantPhaseVoltagesImpl) Voltages() (float64, float64, float64, error) {
return impl.phaseVoltages()
}

View file

@ -24,6 +24,7 @@ import (
"math"
"github.com/evcc-io/evcc/api"
"github.com/evcc-io/evcc/api/implement"
"github.com/evcc-io/evcc/util"
"github.com/evcc-io/evcc/util/modbus"
"github.com/evcc-io/evcc/util/sponsor"
@ -46,6 +47,7 @@ var igyRegMaxCurrents = []uint16{1012, 1014, 1016} // max current per phase
// Innogy is an api.Charger implementation for Innogy eBox wallboxes.
type Innogy struct {
implement.Caps
conn *modbus.Connection
curr float64
hasVoltages bool
@ -70,21 +72,16 @@ func NewInnogyFromConfig(ctx context.Context, other map[string]any) (api.Charger
return nil, err
}
var totalEnergy func() (float64, error)
var voltages func() (float64, float64, float64, error)
// check presence of energy meter & voltages registers
if b, err := wb.conn.ReadInputRegisters(igyRegModbusTableVersion, 1); err == nil && binary.BigEndian.Uint16(b) >= 6 {
totalEnergy = wb.totalEnergy
voltages = wb.voltages
implement.Has(wb, implement.MeterEnergy(wb.totalEnergy))
implement.Has(wb, implement.PhaseVoltages(wb.voltages))
wb.hasVoltages = true
}
return decorateInnogy(wb, totalEnergy, voltages), nil
return wb, nil
}
//go:generate go tool decorate -f decorateInnogy -b *Innogy -r api.Charger -t api.MeterEnergy,api.PhaseVoltages
// NewInnogy creates a Innogy charger
func NewInnogy(ctx context.Context, uri string, id uint8) (*Innogy, error) {
conn, err := modbus.NewConnection(ctx, uri, "", "", 0, modbus.Tcp, id)
@ -100,6 +97,7 @@ func NewInnogy(ctx context.Context, uri string, id uint8) (*Innogy, error) {
conn.Logger(log.TRACE)
wb := &Innogy{
Caps: implement.New(),
conn: conn,
curr: 6,
}

View file

@ -1,56 +0,0 @@
package charger
// Code generated by github.com/evcc-io/evcc/cmd/tools/decorate.go. DO NOT EDIT.
import (
"reflect"
"github.com/evcc-io/evcc/api"
)
func decorateInnogy(base *Innogy, meterEnergy func() (float64, error), phaseVoltages func() (float64, float64, float64, error)) api.Charger {
caps := make(map[reflect.Type]any)
if meterEnergy != nil {
caps[reflect.TypeFor[api.MeterEnergy]()] = &decorateInnogyMeterEnergyImpl{meterEnergy: meterEnergy}
}
if phaseVoltages != nil {
caps[reflect.TypeFor[api.PhaseVoltages]()] = &decorateInnogyPhaseVoltagesImpl{phaseVoltages: phaseVoltages}
}
if len(caps) == 0 {
return base
}
return &decorateInnogyCapable{Innogy: base, caps: caps}
}
type decorateInnogyCapable struct {
*Innogy
caps map[reflect.Type]any
}
func (d *decorateInnogyCapable) Capability(typ reflect.Type) (any, bool) {
c, ok := d.caps[typ]
if !ok && reflect.TypeOf(d).Implements(typ) {
return d, true
}
return c, ok
}
type decorateInnogyMeterEnergyImpl struct {
meterEnergy func() (float64, error)
}
func (impl *decorateInnogyMeterEnergyImpl) TotalEnergy() (float64, error) {
return impl.meterEnergy()
}
type decorateInnogyPhaseVoltagesImpl struct {
phaseVoltages func() (float64, float64, float64, error)
}
func (impl *decorateInnogyPhaseVoltagesImpl) Voltages() (float64, float64, float64, error) {
return impl.phaseVoltages()
}

View file

@ -27,6 +27,7 @@ import (
"time"
"github.com/evcc-io/evcc/api"
"github.com/evcc-io/evcc/api/implement"
"github.com/evcc-io/evcc/util"
"github.com/evcc-io/evcc/util/modbus"
"github.com/evcc-io/evcc/util/sponsor"
@ -39,6 +40,7 @@ import (
// Keba is an api.Charger implementation
type Keba struct {
*embed
implement.Caps
log *util.Logger
conn *modbus.Connection
current uint16
@ -72,8 +74,6 @@ func init() {
registry.AddCtx("keba-modbus", NewKebaFromConfig)
}
//go:generate go tool decorate -f decorateKeba -b *Keba -r api.Charger -t api.Meter,api.MeterEnergy,api.PhaseCurrents,api.Identifier,api.StatusReasoner,api.PhaseSwitcher,api.PhaseGetter
// NewKebaFromConfig creates a new Keba ModbusTCP charger
func NewKebaFromConfig(ctx context.Context, other map[string]any) (api.Charger, error) {
cc := struct {
@ -94,16 +94,6 @@ func NewKebaFromConfig(ctx context.Context, other map[string]any) (api.Charger,
return nil, err
}
// optional features
var (
currentPower, totalEnergy func() (float64, error)
currents func() (float64, float64, float64, error)
identify func() (string, error)
reason func() (api.Reason, error)
phasesS func(int) error
phasesG func() (int, error)
)
b, err := wb.conn.ReadHoldingRegisters(kebaRegProduct, 2)
if err != nil {
return nil, err
@ -140,21 +130,21 @@ func NewKebaFromConfig(ctx context.Context, other map[string]any) (api.Charger,
}
if hasEnergyMeter {
currentPower = wb.currentPower
totalEnergy = wb.totalEnergy
currents = wb.currents
implement.Has(wb, implement.Meter(wb.currentPower))
implement.Has(wb, implement.MeterEnergy(wb.totalEnergy))
implement.Has(wb, implement.PhaseCurrents(wb.currents))
}
if hasRFID {
identify = wb.identify
reason = wb.statusReason
implement.Has(wb, implement.Identifier(wb.identify))
implement.Has(wb, implement.StatusReasoner(wb.statusReason))
}
// phases
if b, err := wb.conn.ReadHoldingRegisters(kebaRegPhaseSource, 2); err == nil {
if source := binary.BigEndian.Uint32(b); source == 3 {
phasesS = wb.phases1p3p
phasesG = wb.getPhases
implement.Has(wb, implement.PhaseSwitcher(wb.phases1p3p))
implement.Has(wb, implement.PhaseGetter(wb.getPhases))
}
}
@ -168,7 +158,7 @@ func NewKebaFromConfig(ctx context.Context, other map[string]any) (api.Charger,
go wb.heartbeat(ctx, u)
}
return decorateKeba(wb, currentPower, totalEnergy, currents, identify, reason, phasesS, phasesG), nil
return wb, nil
}
// NewKeba creates a new charger
@ -187,6 +177,7 @@ func NewKeba(ctx context.Context, embed embed, uri string, slaveID uint8) (*Keba
wb := &Keba{
embed: &embed,
Caps: implement.New(),
log: log,
conn: conn,
regEnable: kebaRegEnable,

View file

@ -1,116 +0,0 @@
package charger
// Code generated by github.com/evcc-io/evcc/cmd/tools/decorate.go. DO NOT EDIT.
import (
"reflect"
"github.com/evcc-io/evcc/api"
)
func decorateKeba(base *Keba, meter func() (float64, error), meterEnergy func() (float64, error), phaseCurrents func() (float64, float64, float64, error), identifier func() (string, error), statusReasoner func() (api.Reason, error), phaseSwitcher func(int) error, phaseGetter func() (int, error)) api.Charger {
caps := make(map[reflect.Type]any)
if meter != nil {
caps[reflect.TypeFor[api.Meter]()] = &decorateKebaMeterImpl{meter: meter}
}
if meterEnergy != nil {
caps[reflect.TypeFor[api.MeterEnergy]()] = &decorateKebaMeterEnergyImpl{meterEnergy: meterEnergy}
}
if phaseCurrents != nil {
caps[reflect.TypeFor[api.PhaseCurrents]()] = &decorateKebaPhaseCurrentsImpl{phaseCurrents: phaseCurrents}
}
if identifier != nil {
caps[reflect.TypeFor[api.Identifier]()] = &decorateKebaIdentifierImpl{identifier: identifier}
}
if statusReasoner != nil {
caps[reflect.TypeFor[api.StatusReasoner]()] = &decorateKebaStatusReasonerImpl{statusReasoner: statusReasoner}
}
if phaseSwitcher != nil {
caps[reflect.TypeFor[api.PhaseSwitcher]()] = &decorateKebaPhaseSwitcherImpl{phaseSwitcher: phaseSwitcher}
}
if phaseGetter != nil {
caps[reflect.TypeFor[api.PhaseGetter]()] = &decorateKebaPhaseGetterImpl{phaseGetter: phaseGetter}
}
if len(caps) == 0 {
return base
}
return &decorateKebaCapable{Keba: base, caps: caps}
}
type decorateKebaCapable struct {
*Keba
caps map[reflect.Type]any
}
func (d *decorateKebaCapable) Capability(typ reflect.Type) (any, bool) {
c, ok := d.caps[typ]
if !ok && reflect.TypeOf(d).Implements(typ) {
return d, true
}
return c, ok
}
type decorateKebaIdentifierImpl struct {
identifier func() (string, error)
}
func (impl *decorateKebaIdentifierImpl) Identify() (string, error) {
return impl.identifier()
}
type decorateKebaMeterImpl struct {
meter func() (float64, error)
}
func (impl *decorateKebaMeterImpl) CurrentPower() (float64, error) {
return impl.meter()
}
type decorateKebaMeterEnergyImpl struct {
meterEnergy func() (float64, error)
}
func (impl *decorateKebaMeterEnergyImpl) TotalEnergy() (float64, error) {
return impl.meterEnergy()
}
type decorateKebaPhaseCurrentsImpl struct {
phaseCurrents func() (float64, float64, float64, error)
}
func (impl *decorateKebaPhaseCurrentsImpl) Currents() (float64, float64, float64, error) {
return impl.phaseCurrents()
}
type decorateKebaPhaseGetterImpl struct {
phaseGetter func() (int, error)
}
func (impl *decorateKebaPhaseGetterImpl) GetPhases() (int, error) {
return impl.phaseGetter()
}
type decorateKebaPhaseSwitcherImpl struct {
phaseSwitcher func(int) error
}
func (impl *decorateKebaPhaseSwitcherImpl) Phases1p3p(p0 int) error {
return impl.phaseSwitcher(p0)
}
type decorateKebaStatusReasonerImpl struct {
statusReasoner func() (api.Reason, error)
}
func (impl *decorateKebaStatusReasonerImpl) StatusReason() (api.Reason, error) {
return impl.statusReasoner()
}

View file

@ -8,6 +8,7 @@ import (
"time"
"github.com/evcc-io/evcc/api"
"github.com/evcc-io/evcc/api/implement"
"github.com/evcc-io/evcc/charger/keba"
"github.com/evcc-io/evcc/util"
)
@ -20,6 +21,7 @@ const (
// KebaUdp is an api.Charger implementation
type KebaUdp struct {
implement.Caps
log *util.Logger
conn string
rfid keba.RFID
@ -32,8 +34,6 @@ func init() {
registry.Add("keba-udp", NewKebaUdpFromConfig)
}
//go:generate go tool decorate -f decorateKebaUdp -b *KebaUdp -r api.Charger -t api.Meter,api.MeterEnergy,api.PhaseCurrents
// NewKebaUdpFromConfig creates a new Keba UDP charger
func NewKebaUdpFromConfig(other map[string]any) (api.Charger, error) {
cc := struct {
@ -60,7 +60,9 @@ func NewKebaUdpFromConfig(other map[string]any) (api.Charger, error) {
}
if energy > 0 {
return decorateKebaUdp(k, k.currentPower, k.totalEnergy, k.currents), nil
implement.Has(k, implement.Meter(k.currentPower))
implement.Has(k, implement.MeterEnergy(k.totalEnergy))
implement.Has(k, implement.PhaseCurrents(k.currents))
}
return k, err
@ -80,6 +82,7 @@ func NewKebaUdp(uri, serial string, rfid keba.RFID, timeout time.Duration) (*Keb
sender, err := keba.NewSender(log, conn)
c := &KebaUdp{
Caps: implement.New(),
log: log,
conn: conn,
rfid: rfid,

View file

@ -1,68 +0,0 @@
package charger
// Code generated by github.com/evcc-io/evcc/cmd/tools/decorate.go. DO NOT EDIT.
import (
"reflect"
"github.com/evcc-io/evcc/api"
)
func decorateKebaUdp(base *KebaUdp, meter func() (float64, error), meterEnergy func() (float64, error), phaseCurrents func() (float64, float64, float64, error)) api.Charger {
caps := make(map[reflect.Type]any)
if meter != nil {
caps[reflect.TypeFor[api.Meter]()] = &decorateKebaUdpMeterImpl{meter: meter}
}
if meterEnergy != nil {
caps[reflect.TypeFor[api.MeterEnergy]()] = &decorateKebaUdpMeterEnergyImpl{meterEnergy: meterEnergy}
}
if phaseCurrents != nil {
caps[reflect.TypeFor[api.PhaseCurrents]()] = &decorateKebaUdpPhaseCurrentsImpl{phaseCurrents: phaseCurrents}
}
if len(caps) == 0 {
return base
}
return &decorateKebaUdpCapable{KebaUdp: base, caps: caps}
}
type decorateKebaUdpCapable struct {
*KebaUdp
caps map[reflect.Type]any
}
func (d *decorateKebaUdpCapable) Capability(typ reflect.Type) (any, bool) {
c, ok := d.caps[typ]
if !ok && reflect.TypeOf(d).Implements(typ) {
return d, true
}
return c, ok
}
type decorateKebaUdpMeterImpl struct {
meter func() (float64, error)
}
func (impl *decorateKebaUdpMeterImpl) CurrentPower() (float64, error) {
return impl.meter()
}
type decorateKebaUdpMeterEnergyImpl struct {
meterEnergy func() (float64, error)
}
func (impl *decorateKebaUdpMeterEnergyImpl) TotalEnergy() (float64, error) {
return impl.meterEnergy()
}
type decorateKebaUdpPhaseCurrentsImpl struct {
phaseCurrents func() (float64, float64, float64, error)
}
func (impl *decorateKebaUdpPhaseCurrentsImpl) Currents() (float64, float64, float64, error) {
return impl.phaseCurrents()
}

View file

@ -23,6 +23,7 @@ import (
"fmt"
"github.com/evcc-io/evcc/api"
"github.com/evcc-io/evcc/api/implement"
"github.com/evcc-io/evcc/util"
"github.com/evcc-io/evcc/util/modbus"
"github.com/evcc-io/evcc/util/sponsor"
@ -30,6 +31,7 @@ import (
// KSE charger implementation
type KSE struct {
implement.Caps
log *util.Logger
conn *modbus.Connection
curr uint16
@ -71,8 +73,6 @@ func NewKSEFromConfig(ctx context.Context, other map[string]any) (api.Charger, e
return NewKSE(ctx, cc.URI, cc.Device, cc.Comset, cc.Baudrate, cc.ID)
}
//go:generate go tool decorate -f decorateKSE -b *KSE -r api.Charger -t api.PhaseSwitcher,api.PhaseGetter,api.Identifier
// NewKSE creates KSE charger
func NewKSE(ctx context.Context, uri, device, comset string, baudrate int, slaveID uint8) (api.Charger, error) {
conn, err := modbus.NewConnection(ctx, uri, device, comset, baudrate, modbus.Rtu, slaveID)
@ -88,31 +88,26 @@ func NewKSE(ctx context.Context, uri, device, comset string, baudrate int, slave
conn.Logger(log.TRACE)
wb := &KSE{
Caps: implement.New(),
log: log,
conn: conn,
curr: 6, // assume min current
}
var (
phases1p3p func(int) error
getPhases func() (int, error)
identify func() (string, error)
)
// check presence of 1p3p switching
if b, err := wb.conn.ReadInputRegisters(kseRegFirmwareVersion, 1); err == nil && b[0] >= 0x52 { // >= HW Rev „R“
if b, err := wb.conn.ReadInputRegisters(kseRegFirmwareVersion, 1); err == nil && b[0] >= 0x52 { // >= HW Rev „R”
wb.has1p3p = true
phases1p3p = wb.phases1p3p
getPhases = wb.getPhases
implement.Has(wb, implement.PhaseSwitcher(wb.phases1p3p))
implement.Has(wb, implement.PhaseGetter(wb.getPhases))
}
// check presence of rfid
if b, err := wb.conn.ReadDiscreteInputs(kseRegRFIDinstalled, 1); err == nil && b[0] != 0 {
wb.hasRfid = true
identify = wb.identify
implement.Has(wb, implement.Identifier(wb.identify))
}
return decorateKSE(wb, phases1p3p, getPhases, identify), nil
return wb, nil
}
// Status implements the api.Charger interface

View file

@ -1,68 +0,0 @@
package charger
// Code generated by github.com/evcc-io/evcc/cmd/tools/decorate.go. DO NOT EDIT.
import (
"reflect"
"github.com/evcc-io/evcc/api"
)
func decorateKSE(base *KSE, phaseSwitcher func(int) error, phaseGetter func() (int, error), identifier func() (string, error)) api.Charger {
caps := make(map[reflect.Type]any)
if phaseSwitcher != nil {
caps[reflect.TypeFor[api.PhaseSwitcher]()] = &decorateKSEPhaseSwitcherImpl{phaseSwitcher: phaseSwitcher}
}
if phaseGetter != nil {
caps[reflect.TypeFor[api.PhaseGetter]()] = &decorateKSEPhaseGetterImpl{phaseGetter: phaseGetter}
}
if identifier != nil {
caps[reflect.TypeFor[api.Identifier]()] = &decorateKSEIdentifierImpl{identifier: identifier}
}
if len(caps) == 0 {
return base
}
return &decorateKSECapable{KSE: base, caps: caps}
}
type decorateKSECapable struct {
*KSE
caps map[reflect.Type]any
}
func (d *decorateKSECapable) Capability(typ reflect.Type) (any, bool) {
c, ok := d.caps[typ]
if !ok && reflect.TypeOf(d).Implements(typ) {
return d, true
}
return c, ok
}
type decorateKSEIdentifierImpl struct {
identifier func() (string, error)
}
func (impl *decorateKSEIdentifierImpl) Identify() (string, error) {
return impl.identifier()
}
type decorateKSEPhaseGetterImpl struct {
phaseGetter func() (int, error)
}
func (impl *decorateKSEPhaseGetterImpl) GetPhases() (int, error) {
return impl.phaseGetter()
}
type decorateKSEPhaseSwitcherImpl struct {
phaseSwitcher func(int) error
}
func (impl *decorateKSEPhaseSwitcherImpl) Phases1p3p(p0 int) error {
return impl.phaseSwitcher(p0)
}

View file

@ -25,6 +25,7 @@ import (
"time"
"github.com/evcc-io/evcc/api"
"github.com/evcc-io/evcc/api/implement"
"github.com/evcc-io/evcc/util"
"github.com/evcc-io/evcc/util/modbus"
"github.com/evcc-io/evcc/util/sponsor"
@ -33,6 +34,7 @@ import (
// MennekesCompact is an api.Charger implementation
type MennekesCompact struct {
implement.Caps
log *util.Logger
conn *modbus.Connection
}
@ -87,8 +89,6 @@ func NewMennekesCompactFromConfig(ctx context.Context, other map[string]any) (ap
return NewMennekesCompact(ctx, cc.URI, cc.Device, cc.Comset, cc.Baudrate, cc.Settings.Protocol(), cc.ID, cc.Timeout)
}
//go:generate go tool decorate -f decorateMennekesCompact -b *MennekesCompact -r api.Charger -t api.PhaseSwitcher
// NewMennekesCompact creates Mennekes charger
func NewMennekesCompact(ctx context.Context, uri, device, comset string, baudrate int, proto modbus.Protocol, slaveID uint8, timeout time.Duration) (api.Charger, error) {
conn, err := modbus.NewConnection(ctx, uri, device, comset, baudrate, proto, slaveID)
@ -108,20 +108,20 @@ func NewMennekesCompact(ctx context.Context, uri, device, comset string, baudrat
conn.Logger(log.TRACE)
wb := &MennekesCompact{
Caps: implement.New(),
log: log,
conn: conn,
}
// check phase switching support
var phasesS func(int) error
if b, err := wb.conn.ReadHoldingRegisters(mennekesRegPhaseOptionsHW, 1); err == nil && encoding.Uint16(b) == 2 {
phasesS = wb.phases1p3p
implement.Has(wb, implement.PhaseSwitcher(wb.phases1p3p))
}
// failsafe
go wb.heartbeat(ctx, mennekesHeartbeatInterval)
return decorateMennekesCompact(wb, phasesS), nil
return wb, nil
}
func (wb *MennekesCompact) heartbeat(ctx context.Context, timeout time.Duration) {

View file

@ -1,44 +0,0 @@
package charger
// Code generated by github.com/evcc-io/evcc/cmd/tools/decorate.go. DO NOT EDIT.
import (
"reflect"
"github.com/evcc-io/evcc/api"
)
func decorateMennekesCompact(base *MennekesCompact, phaseSwitcher func(int) error) api.Charger {
caps := make(map[reflect.Type]any)
if phaseSwitcher != nil {
caps[reflect.TypeFor[api.PhaseSwitcher]()] = &decorateMennekesCompactPhaseSwitcherImpl{phaseSwitcher: phaseSwitcher}
}
if len(caps) == 0 {
return base
}
return &decorateMennekesCompactCapable{MennekesCompact: base, caps: caps}
}
type decorateMennekesCompactCapable struct {
*MennekesCompact
caps map[reflect.Type]any
}
func (d *decorateMennekesCompactCapable) Capability(typ reflect.Type) (any, bool) {
c, ok := d.caps[typ]
if !ok && reflect.TypeOf(d).Implements(typ) {
return d, true
}
return c, ok
}
type decorateMennekesCompactPhaseSwitcherImpl struct {
phaseSwitcher func(int) error
}
func (impl *decorateMennekesCompactPhaseSwitcherImpl) Phases1p3p(p0 int) error {
return impl.phaseSwitcher(p0)
}

View file

@ -1,35 +0,0 @@
package charger
// Code generated by github.com/evcc-io/evcc/cmd/tools/decorate.go. DO NOT EDIT.
import (
"github.com/evcc-io/evcc/api"
)
func decorateNexblue(base *Nexblue, phaseSwitcher func(int) error) api.Charger {
switch {
case phaseSwitcher == nil:
return base
case phaseSwitcher != nil:
return &struct {
*Nexblue
api.PhaseSwitcher
}{
Nexblue: base,
PhaseSwitcher: &decorateNexbluePhaseSwitcherImpl{
phaseSwitcher: phaseSwitcher,
},
}
}
return nil
}
type decorateNexbluePhaseSwitcherImpl struct {
phaseSwitcher func(int) error
}
func (impl *decorateNexbluePhaseSwitcherImpl) Phases1p3p(p0 int) error {
return impl.phaseSwitcher(p0)
}

View file

@ -7,6 +7,7 @@ import (
"math"
"github.com/evcc-io/evcc/api"
"github.com/evcc-io/evcc/api/implement"
"github.com/evcc-io/evcc/util"
"github.com/evcc-io/evcc/util/modbus"
"github.com/evcc-io/evcc/util/sponsor"
@ -18,6 +19,7 @@ import (
// NRGKickGen2 charger implementation
type NRGKickGen2 struct {
implement.Caps
conn *modbus.Connection
}
@ -49,8 +51,6 @@ func init() {
registry.AddCtx("nrggen2", NewNRGKickGen2FromConfig)
}
//go:generate go tool decorate -f decorateNRGKickGen2 -b *NRGKickGen2 -r api.Charger -t api.PhaseSwitcher
// NewNRGKickGen2FromConfig creates a NRGKickGen2 charger from generic config
func NewNRGKickGen2FromConfig(ctx context.Context, other map[string]any) (api.Charger, error) {
cc := struct {
@ -72,17 +72,16 @@ func NewNRGKickGen2FromConfig(ctx context.Context, other map[string]any) (api.Ch
return nil, err
}
var phasesS func(int) error
if cc.Phases1p3p {
// user could have an adapter plug which doesn't support 3 phases
if b, err := nrg.conn.ReadHoldingRegisters(nrgKickGen2MaxPhases, 1); err == nil {
if maxPhases := encoding.Uint16(b); maxPhases > 1 {
phasesS = nrg.phases1p3p
implement.Has(nrg, implement.PhaseSwitcher(nrg.phases1p3p))
}
}
}
return decorateNRGKickGen2(nrg, phasesS), nil
return nrg, nil
}
// NewNRGKickGen2 creates NRGKickGen2 charger
@ -100,6 +99,7 @@ func NewNRGKickGen2(ctx context.Context, uri string, slaveID uint8) (*NRGKickGen
conn.Logger(log.TRACE)
nrg := &NRGKickGen2{
Caps: implement.New(),
conn: conn,
}

View file

@ -1,44 +0,0 @@
package charger
// Code generated by github.com/evcc-io/evcc/cmd/tools/decorate.go. DO NOT EDIT.
import (
"reflect"
"github.com/evcc-io/evcc/api"
)
func decorateNRGKickGen2(base *NRGKickGen2, phaseSwitcher func(int) error) api.Charger {
caps := make(map[reflect.Type]any)
if phaseSwitcher != nil {
caps[reflect.TypeFor[api.PhaseSwitcher]()] = &decorateNRGKickGen2PhaseSwitcherImpl{phaseSwitcher: phaseSwitcher}
}
if len(caps) == 0 {
return base
}
return &decorateNRGKickGen2Capable{NRGKickGen2: base, caps: caps}
}
type decorateNRGKickGen2Capable struct {
*NRGKickGen2
caps map[reflect.Type]any
}
func (d *decorateNRGKickGen2Capable) Capability(typ reflect.Type) (any, bool) {
c, ok := d.caps[typ]
if !ok && reflect.TypeOf(d).Implements(typ) {
return d, true
}
return c, ok
}
type decorateNRGKickGen2PhaseSwitcherImpl struct {
phaseSwitcher func(int) error
}
func (impl *decorateNRGKickGen2PhaseSwitcherImpl) Phases1p3p(p0 int) error {
return impl.phaseSwitcher(p0)
}

View file

@ -26,6 +26,7 @@ import (
"time"
"github.com/evcc-io/evcc/api"
"github.com/evcc-io/evcc/api/implement"
"github.com/evcc-io/evcc/charger/ocpp"
"github.com/evcc-io/evcc/core/loadpoint"
"github.com/evcc-io/evcc/util"
@ -37,6 +38,7 @@ import (
// OCPP charger implementation
type OCPP struct {
implement.Caps
log *util.Logger
cp *ocpp.CP
conn *ocpp.Connector
@ -102,46 +104,37 @@ func NewOCPPFromConfig(ctx context.Context, other map[string]any) (api.Charger,
return nil, api.ErrSponsorRequired
}
var (
powerG, totalEnergyG, socG func() (float64, error)
currentsG, voltagesG func() (float64, float64, float64, error)
)
if c.cp.HasMeasurement(types.MeasurandPowerActiveImport) {
powerG = c.conn.CurrentPower
implement.Has(c, implement.Meter(c.conn.CurrentPower))
}
if c.cp.HasMeasurement(types.MeasurandEnergyActiveImportRegister) {
totalEnergyG = c.conn.TotalEnergy
implement.Has(c, implement.MeterEnergy(c.conn.TotalEnergy))
}
if c.cp.HasMeasurement(types.MeasurandCurrentImport) {
currentsG = c.conn.Currents
implement.Has(c, implement.PhaseCurrents(c.conn.Currents))
}
if c.cp.HasMeasurement(types.MeasurandVoltage) {
voltagesG = c.conn.Voltages
implement.Has(c, implement.PhaseVoltages(c.conn.Voltages))
}
if c.cp.HasMeasurement(types.MeasurandSoC) {
socG = c.conn.Soc
implement.Has(c, implement.Battery(c.conn.Soc))
}
var phasesS func(int) error
if c.cp.PhaseSwitching {
phasesS = c.phases1p3p
implement.Has(c, implement.PhaseSwitcher(c.phases1p3p))
}
var currentG func() (float64, error)
if c.cp.HasMeasurement(types.MeasurandCurrentOffered) {
currentG = c.conn.GetMaxCurrent
implement.Has(c, implement.CurrentGetter(c.conn.GetMaxCurrent))
}
return decorateOCPP(c, powerG, totalEnergyG, currentsG, voltagesG, currentG, phasesS, socG), nil
return c, nil
}
//go:generate go tool decorate -f decorateOCPP -b *OCPP -r api.Charger -t api.Meter,api.MeterEnergy,api.PhaseCurrents,api.PhaseVoltages,api.CurrentGetter,api.PhaseSwitcher,api.Battery
// NewOCPP creates OCPP charger
func NewOCPP(ctx context.Context,
id string, connector int, idTag string,
@ -187,6 +180,7 @@ func NewOCPP(ctx context.Context,
}
c := &OCPP{
Caps: implement.New(),
log: log,
cp: cp,
conn: conn,

View file

@ -1,116 +0,0 @@
package charger
// Code generated by github.com/evcc-io/evcc/cmd/tools/decorate.go. DO NOT EDIT.
import (
"reflect"
"github.com/evcc-io/evcc/api"
)
func decorateOCPP(base *OCPP, meter func() (float64, error), meterEnergy func() (float64, error), phaseCurrents func() (float64, float64, float64, error), phaseVoltages func() (float64, float64, float64, error), currentGetter func() (float64, error), phaseSwitcher func(int) error, battery func() (float64, error)) api.Charger {
caps := make(map[reflect.Type]any)
if meter != nil {
caps[reflect.TypeFor[api.Meter]()] = &decorateOCPPMeterImpl{meter: meter}
}
if meterEnergy != nil {
caps[reflect.TypeFor[api.MeterEnergy]()] = &decorateOCPPMeterEnergyImpl{meterEnergy: meterEnergy}
}
if phaseCurrents != nil {
caps[reflect.TypeFor[api.PhaseCurrents]()] = &decorateOCPPPhaseCurrentsImpl{phaseCurrents: phaseCurrents}
}
if phaseVoltages != nil {
caps[reflect.TypeFor[api.PhaseVoltages]()] = &decorateOCPPPhaseVoltagesImpl{phaseVoltages: phaseVoltages}
}
if currentGetter != nil {
caps[reflect.TypeFor[api.CurrentGetter]()] = &decorateOCPPCurrentGetterImpl{currentGetter: currentGetter}
}
if phaseSwitcher != nil {
caps[reflect.TypeFor[api.PhaseSwitcher]()] = &decorateOCPPPhaseSwitcherImpl{phaseSwitcher: phaseSwitcher}
}
if battery != nil {
caps[reflect.TypeFor[api.Battery]()] = &decorateOCPPBatteryImpl{battery: battery}
}
if len(caps) == 0 {
return base
}
return &decorateOCPPCapable{OCPP: base, caps: caps}
}
type decorateOCPPCapable struct {
*OCPP
caps map[reflect.Type]any
}
func (d *decorateOCPPCapable) Capability(typ reflect.Type) (any, bool) {
c, ok := d.caps[typ]
if !ok && reflect.TypeOf(d).Implements(typ) {
return d, true
}
return c, ok
}
type decorateOCPPBatteryImpl struct {
battery func() (float64, error)
}
func (impl *decorateOCPPBatteryImpl) Soc() (float64, error) {
return impl.battery()
}
type decorateOCPPCurrentGetterImpl struct {
currentGetter func() (float64, error)
}
func (impl *decorateOCPPCurrentGetterImpl) GetMaxCurrent() (float64, error) {
return impl.currentGetter()
}
type decorateOCPPMeterImpl struct {
meter func() (float64, error)
}
func (impl *decorateOCPPMeterImpl) CurrentPower() (float64, error) {
return impl.meter()
}
type decorateOCPPMeterEnergyImpl struct {
meterEnergy func() (float64, error)
}
func (impl *decorateOCPPMeterEnergyImpl) TotalEnergy() (float64, error) {
return impl.meterEnergy()
}
type decorateOCPPPhaseCurrentsImpl struct {
phaseCurrents func() (float64, float64, float64, error)
}
func (impl *decorateOCPPPhaseCurrentsImpl) Currents() (float64, float64, float64, error) {
return impl.phaseCurrents()
}
type decorateOCPPPhaseSwitcherImpl struct {
phaseSwitcher func(int) error
}
func (impl *decorateOCPPPhaseSwitcherImpl) Phases1p3p(p0 int) error {
return impl.phaseSwitcher(p0)
}
type decorateOCPPPhaseVoltagesImpl struct {
phaseVoltages func() (float64, float64, float64, error)
}
func (impl *decorateOCPPPhaseVoltagesImpl) Voltages() (float64, float64, float64, error) {
return impl.phaseVoltages()
}

View file

@ -9,6 +9,7 @@ import (
"time"
"github.com/evcc-io/evcc/api"
"github.com/evcc-io/evcc/api/implement"
"github.com/evcc-io/evcc/charger/openevse"
"github.com/evcc-io/evcc/util"
"github.com/evcc-io/evcc/util/request"
@ -18,6 +19,7 @@ import (
// OpenEVSE charger implementation
type OpenEVSE struct {
*request.Helper
implement.Caps
uri string
statusG util.Cacheable[openevse.Status]
current int
@ -28,8 +30,6 @@ func init() {
registry.Add("openevse", NewOpenEVSEFromConfig)
}
//go:generate go tool decorate -f decorateOpenEVSE -b *OpenEVSE -r api.Charger -t api.PhaseSwitcher
// NewOpenEVSEFromConfig creates an OpenEVSE charger from generic config
func NewOpenEVSEFromConfig(other map[string]any) (api.Charger, error) {
cc := struct {
@ -59,6 +59,7 @@ func NewOpenEVSE(uri, user, password string, cache time.Duration) (api.Charger,
c := &OpenEVSE{
Helper: request.NewHelper(log),
Caps: implement.New(),
uri: util.DefaultScheme(strings.TrimSuffix(uri, "/"), "http"),
}
@ -75,9 +76,8 @@ func NewOpenEVSE(uri, user, password string, cache time.Duration) (api.Charger,
return res, err
}, cache)
var phases1p3p func(int) error
if err := c.hasPhaseSwitchCapabilities(); err == nil {
phases1p3p = c.phases1p3p
implement.Has(c, implement.PhaseSwitcher(c.phases1p3p))
// disable EVSE's own 1/3-phase auto-switching
if err := c.rapiCommand("$S8 0"); err != nil {
@ -85,7 +85,7 @@ func NewOpenEVSE(uri, user, password string, cache time.Duration) (api.Charger,
}
}
return decorateOpenEVSE(c, phases1p3p), nil
return c, nil
}
func (c *OpenEVSE) setOverride() error {

View file

@ -1,44 +0,0 @@
package charger
// Code generated by github.com/evcc-io/evcc/cmd/tools/decorate.go. DO NOT EDIT.
import (
"reflect"
"github.com/evcc-io/evcc/api"
)
func decorateOpenEVSE(base *OpenEVSE, phaseSwitcher func(int) error) api.Charger {
caps := make(map[reflect.Type]any)
if phaseSwitcher != nil {
caps[reflect.TypeFor[api.PhaseSwitcher]()] = &decorateOpenEVSEPhaseSwitcherImpl{phaseSwitcher: phaseSwitcher}
}
if len(caps) == 0 {
return base
}
return &decorateOpenEVSECapable{OpenEVSE: base, caps: caps}
}
type decorateOpenEVSECapable struct {
*OpenEVSE
caps map[reflect.Type]any
}
func (d *decorateOpenEVSECapable) Capability(typ reflect.Type) (any, bool) {
c, ok := d.caps[typ]
if !ok && reflect.TypeOf(d).Implements(typ) {
return d, true
}
return c, ok
}
type decorateOpenEVSEPhaseSwitcherImpl struct {
phaseSwitcher func(int) error
}
func (impl *decorateOpenEVSEPhaseSwitcherImpl) Phases1p3p(p0 int) error {
return impl.phaseSwitcher(p0)
}

View file

@ -6,12 +6,14 @@ import (
"fmt"
"github.com/evcc-io/evcc/api"
"github.com/evcc-io/evcc/api/implement"
"github.com/evcc-io/evcc/util"
"github.com/evcc-io/evcc/util/modbus"
)
// OpenWB20 charger implementation
type OpenWB20 struct {
implement.Caps
conn *modbus.Connection
enabled bool
curr uint16
@ -41,8 +43,6 @@ func init() {
// https://openwb.de/main/wp-content/uploads/2023/10/ModbusTCP-openWB-series2-Pro-1.pdf
//go:generate go tool decorate -f decorateOpenWB20 -b *OpenWB20 -r api.Charger -t api.PhaseSwitcher,api.Identifier
// NewOpenWB20FromConfig creates a OpenWB20 charger from generic config
func NewOpenWB20FromConfig(ctx context.Context, other map[string]any) (api.Charger, error) {
cc := struct {
@ -66,17 +66,15 @@ func NewOpenWB20FromConfig(ctx context.Context, other map[string]any) (api.Charg
return nil, err
}
var phases1p3p func(int) error
if cc.Phases1p3p {
phases1p3p = wb.phases1p3p
implement.Has(wb, implement.PhaseSwitcher(wb.phases1p3p))
}
var identify func() (string, error)
if cc.Identify {
identify = wb.identify
implement.Has(wb, implement.Identifier(wb.identify))
}
return decorateOpenWB20(wb, phases1p3p, identify), nil
return wb, nil
}
// NewOpenWB20 creates OpenWB20 charger
@ -92,6 +90,7 @@ func NewOpenWB20(ctx context.Context, uri string, slaveID uint8, connector uint1
conn.Logger(log.TRACE)
wb := &OpenWB20{
Caps: implement.New(),
conn: conn,
curr: 6 * 100,
base: (connector - 1) * 100,

View file

@ -1,56 +0,0 @@
package charger
// Code generated by github.com/evcc-io/evcc/cmd/tools/decorate.go. DO NOT EDIT.
import (
"reflect"
"github.com/evcc-io/evcc/api"
)
func decorateOpenWB20(base *OpenWB20, phaseSwitcher func(int) error, identifier func() (string, error)) api.Charger {
caps := make(map[reflect.Type]any)
if phaseSwitcher != nil {
caps[reflect.TypeFor[api.PhaseSwitcher]()] = &decorateOpenWB20PhaseSwitcherImpl{phaseSwitcher: phaseSwitcher}
}
if identifier != nil {
caps[reflect.TypeFor[api.Identifier]()] = &decorateOpenWB20IdentifierImpl{identifier: identifier}
}
if len(caps) == 0 {
return base
}
return &decorateOpenWB20Capable{OpenWB20: base, caps: caps}
}
type decorateOpenWB20Capable struct {
*OpenWB20
caps map[reflect.Type]any
}
func (d *decorateOpenWB20Capable) Capability(typ reflect.Type) (any, bool) {
c, ok := d.caps[typ]
if !ok && reflect.TypeOf(d).Implements(typ) {
return d, true
}
return c, ok
}
type decorateOpenWB20IdentifierImpl struct {
identifier func() (string, error)
}
func (impl *decorateOpenWB20IdentifierImpl) Identify() (string, error) {
return impl.identifier()
}
type decorateOpenWB20PhaseSwitcherImpl struct {
phaseSwitcher func(int) error
}
func (impl *decorateOpenWB20PhaseSwitcherImpl) Phases1p3p(p0 int) error {
return impl.phaseSwitcher(p0)
}

View file

@ -1,68 +0,0 @@
package charger
// Code generated by github.com/evcc-io/evcc/cmd/tools/decorate.go. DO NOT EDIT.
import (
"reflect"
"github.com/evcc-io/evcc/api"
)
func decorateOpenWbNative(base *OpenWbNative, chargerEx func(float64) error, phaseSwitcher func(int) error, identifier func() (string, error)) api.Charger {
caps := make(map[reflect.Type]any)
if chargerEx != nil {
caps[reflect.TypeFor[api.ChargerEx]()] = &decorateOpenWbNativeChargerExImpl{chargerEx: chargerEx}
}
if phaseSwitcher != nil {
caps[reflect.TypeFor[api.PhaseSwitcher]()] = &decorateOpenWbNativePhaseSwitcherImpl{phaseSwitcher: phaseSwitcher}
}
if identifier != nil {
caps[reflect.TypeFor[api.Identifier]()] = &decorateOpenWbNativeIdentifierImpl{identifier: identifier}
}
if len(caps) == 0 {
return base
}
return &decorateOpenWbNativeCapable{OpenWbNative: base, caps: caps}
}
type decorateOpenWbNativeCapable struct {
*OpenWbNative
caps map[reflect.Type]any
}
func (d *decorateOpenWbNativeCapable) Capability(typ reflect.Type) (any, bool) {
c, ok := d.caps[typ]
if !ok && reflect.TypeOf(d).Implements(typ) {
return d, true
}
return c, ok
}
type decorateOpenWbNativeChargerExImpl struct {
chargerEx func(float64) error
}
func (impl *decorateOpenWbNativeChargerExImpl) MaxCurrentMillis(p0 float64) error {
return impl.chargerEx(p0)
}
type decorateOpenWbNativeIdentifierImpl struct {
identifier func() (string, error)
}
func (impl *decorateOpenWbNativeIdentifierImpl) Identify() (string, error) {
return impl.identifier()
}
type decorateOpenWbNativePhaseSwitcherImpl struct {
phaseSwitcher func(int) error
}
func (impl *decorateOpenWbNativePhaseSwitcherImpl) Phases1p3p(p0 int) error {
return impl.phaseSwitcher(p0)
}

View file

@ -6,6 +6,7 @@ import (
"time"
"github.com/evcc-io/evcc/api"
"github.com/evcc-io/evcc/api/implement"
"github.com/evcc-io/evcc/charger/openwb/native"
"github.com/evcc-io/evcc/util"
"github.com/evcc-io/evcc/util/modbus"
@ -24,6 +25,7 @@ type openWbGpioLines struct {
// OpenWbNative charger implementation
type OpenWbNative struct {
api.Charger
implement.Caps
log *util.Logger
rfId native.RfIdContainer
cpWait time.Duration
@ -42,8 +44,6 @@ func init() {
registry.AddCtx("openwb-native", NewOpenWbNativeFromConfig)
}
//go:generate go tool decorate -o openwb-native_decorators_linux.go -f decorateOpenWbNative -b *OpenWbNative -r api.Charger -t api.ChargerEx,api.PhaseSwitcher,api.Identifier
// NewOpenWbNativeFromConfig creates an OpenWbNative charger from generic config
func NewOpenWbNativeFromConfig(ctx context.Context, other map[string]any) (api.Charger, error) {
cc := struct {
@ -87,25 +87,20 @@ func NewOpenWbNative(ctx context.Context, uri, device, comset string, baudrate i
wb := &OpenWbNative{
Charger: evse,
Caps: implement.New(),
log: log,
cpWait: cpWait,
connector: connector,
chargeState: api.StatusNone,
}
var (
phases1p3p func(int) error
identify func() (string, error)
maxCurrentMillis func(float64) error
)
if ex, ok := api.Cap[api.ChargerEx](evse); ok {
maxCurrentMillis = ex.MaxCurrentMillis
implement.May(wb, implement.ChargerEx(ex.MaxCurrentMillis))
}
// configure special external hardware features
if hasPhases1p3p {
phases1p3p = wb.phases1p3p
implement.May(wb, implement.PhaseSwitcher(wb.phases1p3p))
}
if rfIdVidPid != "" {
@ -113,8 +108,7 @@ func NewOpenWbNative(ctx context.Context, uri, device, comset string, baudrate i
if err != nil {
return nil, err
}
identify = wb.identify
implement.May(wb, implement.Identifier(wb.identify))
}
// initialize GPIO lines and set pins to output
@ -141,7 +135,7 @@ func NewOpenWbNative(ctx context.Context, uri, device, comset string, baudrate i
wb.gpio.ph3.Close()
}()
return decorateOpenWbNative(wb, maxCurrentMillis, phases1p3p, identify), nil
return wb, nil
}
// Status implements the api.Charger interface

View file

@ -7,6 +7,7 @@ import (
"time"
"github.com/evcc-io/evcc/api"
"github.com/evcc-io/evcc/api/implement"
"github.com/evcc-io/evcc/charger/openwb/pro"
"github.com/evcc-io/evcc/util"
"github.com/evcc-io/evcc/util/request"
@ -16,12 +17,11 @@ func init() {
registry.AddCtx("openwbpro", NewOpenWBProFromConfig)
}
//go:generate go tool decorate -f decorateOpenWBPro -b *OpenWBPro -r api.Charger -t api.Resurrector
// https://openwb.de/main/?page_id=771
// OpenWBPro charger implementation
type OpenWBPro struct {
implement.Caps
*request.Helper
uri string
current float64
@ -51,12 +51,11 @@ func NewOpenWBProFromConfig(ctx context.Context, other map[string]any) (api.Char
return nil, err
}
var wakeup func() error
if status.Version >= 9 {
wakeup = wb.wakeup
implement.Has(wb, implement.Resurrector(wb.wakeup))
}
return decorateOpenWBPro(wb, wakeup), nil
return wb, nil
}
// NewOpenWBPro creates OpenWBPro charger
@ -64,6 +63,7 @@ func NewOpenWBPro(ctx context.Context, uri string, cache time.Duration) (*OpenWB
log := util.NewLogger("owbpro")
wb := &OpenWBPro{
Caps: implement.New(),
Helper: request.NewHelper(log),
uri: strings.TrimRight(uri, "/"),
current: 6, // 6A defined value

View file

@ -1,44 +0,0 @@
package charger
// Code generated by github.com/evcc-io/evcc/cmd/tools/decorate.go. DO NOT EDIT.
import (
"reflect"
"github.com/evcc-io/evcc/api"
)
func decorateOpenWBPro(base *OpenWBPro, resurrector func() error) api.Charger {
caps := make(map[reflect.Type]any)
if resurrector != nil {
caps[reflect.TypeFor[api.Resurrector]()] = &decorateOpenWBProResurrectorImpl{resurrector: resurrector}
}
if len(caps) == 0 {
return base
}
return &decorateOpenWBProCapable{OpenWBPro: base, caps: caps}
}
type decorateOpenWBProCapable struct {
*OpenWBPro
caps map[reflect.Type]any
}
func (d *decorateOpenWBProCapable) Capability(typ reflect.Type) (any, bool) {
c, ok := d.caps[typ]
if !ok && reflect.TypeOf(d).Implements(typ) {
return d, true
}
return c, ok
}
type decorateOpenWBProResurrectorImpl struct {
resurrector func() error
}
func (impl *decorateOpenWBProResurrectorImpl) WakeUp() error {
return impl.resurrector()
}

View file

@ -7,6 +7,7 @@ import (
"time"
"github.com/evcc-io/evcc/api"
"github.com/evcc-io/evcc/api/implement"
"github.com/evcc-io/evcc/charger/openwb"
"github.com/evcc-io/evcc/plugin"
"github.com/evcc-io/evcc/plugin/mqtt"
@ -19,6 +20,7 @@ func init() {
// OpenWB configures generic charger and charge meter for an openWB loadpoint
type OpenWB struct {
implement.Caps
current int64
enabled bool
statusG func() (string, error)
@ -30,8 +32,6 @@ type OpenWB struct {
authS func(string) error
}
//go:generate go tool decorate -f decorateOpenWB -b *OpenWB -r api.Charger -t api.PhaseSwitcher,api.Battery
// NewOpenWBFromConfig creates a new configurable charger
func NewOpenWBFromConfig(other map[string]any) (api.Charger, error) {
cc := struct {
@ -145,6 +145,7 @@ func NewOpenWB(log *util.Logger, mqttconf mqtt.Config, id int, topic string, p1p
}
c := &OpenWB{
Caps: implement.New(),
currentS: currentS,
statusG: statusG,
currentPowerG: currentPowerG,
@ -171,7 +172,6 @@ func NewOpenWB(log *util.Logger, mqttconf mqtt.Config, id int, topic string, p1p
// optional capabilities
var phases func(int) error
if p1p3 {
phasesTopic := openwb.SlavePhasesTopic
if id == 2 {
@ -184,20 +184,20 @@ func NewOpenWB(log *util.Logger, mqttconf mqtt.Config, id int, topic string, p1p
return nil, err
}
phases = func(phases int) error {
implement.Has(c, implement.PhaseSwitcher(func(phases int) error {
return phasesS(int64(phases))
}
}))
}
var soc func() (float64, error)
if dc {
soc, err = to.FloatGetter(mq(openwb.VehicleSocTopic))
soc, err := to.FloatGetter(mq(openwb.VehicleSocTopic))
if err != nil {
return nil, err
}
implement.Has(c, implement.Battery(soc))
}
return decorateOpenWB(c, phases, soc), nil
return c, nil
}
func (m *OpenWB) Enable(enable bool) error {

View file

@ -1,56 +0,0 @@
package charger
// Code generated by github.com/evcc-io/evcc/cmd/tools/decorate.go. DO NOT EDIT.
import (
"reflect"
"github.com/evcc-io/evcc/api"
)
func decorateOpenWB(base *OpenWB, phaseSwitcher func(int) error, battery func() (float64, error)) api.Charger {
caps := make(map[reflect.Type]any)
if phaseSwitcher != nil {
caps[reflect.TypeFor[api.PhaseSwitcher]()] = &decorateOpenWBPhaseSwitcherImpl{phaseSwitcher: phaseSwitcher}
}
if battery != nil {
caps[reflect.TypeFor[api.Battery]()] = &decorateOpenWBBatteryImpl{battery: battery}
}
if len(caps) == 0 {
return base
}
return &decorateOpenWBCapable{OpenWB: base, caps: caps}
}
type decorateOpenWBCapable struct {
*OpenWB
caps map[reflect.Type]any
}
func (d *decorateOpenWBCapable) Capability(typ reflect.Type) (any, bool) {
c, ok := d.caps[typ]
if !ok && reflect.TypeOf(d).Implements(typ) {
return d, true
}
return c, ok
}
type decorateOpenWBBatteryImpl struct {
battery func() (float64, error)
}
func (impl *decorateOpenWBBatteryImpl) Soc() (float64, error) {
return impl.battery()
}
type decorateOpenWBPhaseSwitcherImpl struct {
phaseSwitcher func(int) error
}
func (impl *decorateOpenWBPhaseSwitcherImpl) Phases1p3p(p0 int) error {
return impl.phaseSwitcher(p0)
}

View file

@ -6,6 +6,7 @@ import (
"strings"
"github.com/evcc-io/evcc/api"
"github.com/evcc-io/evcc/api/implement"
"github.com/evcc-io/evcc/charger/pcelectric"
"github.com/evcc-io/evcc/util"
"github.com/evcc-io/evcc/util/request"
@ -14,6 +15,7 @@ import (
// PCElectric charger implementation
type PCElectric struct {
implement.Caps
*request.Helper
log *util.Logger
@ -30,8 +32,6 @@ func init() {
registry.Add("pcelectric", NewPCElectricFromConfig)
}
//go:generate go tool decorate -f decoratePCE -b *PCElectric -r api.Charger -t api.Meter,api.MeterEnergy,api.PhaseCurrents
// NewPCElectricFromConfig creates a PCElectric charger from generic config
func NewPCElectricFromConfig(other map[string]any) (api.Charger, error) {
cc := struct {
@ -50,7 +50,10 @@ func NewPCElectricFromConfig(other map[string]any) (api.Charger, error) {
if err == nil && wb.slaveIndex == 0 { // Nur Master hat den Zähler...leider
var res pcelectric.MeterInfo
if err := wb.GetJSON(wb.meter, &res); err == nil && res.MeterSerial != "" {
return decoratePCE(wb, wb.currentPower, wb.totalEnergy, wb.currents), nil
implement.Has(wb, implement.Meter(wb.currentPower))
implement.Has(wb, implement.MeterEnergy(wb.totalEnergy))
implement.Has(wb, implement.PhaseCurrents(wb.currents))
return wb, nil
}
wb.meter = ""
@ -69,6 +72,7 @@ func NewPCElectric(uri string, slaveIndex int, meter string) (*PCElectric, error
}
wb := &PCElectric{
Caps: implement.New(),
Helper: request.NewHelper(log),
log: log,
uri: uri,

View file

@ -1,68 +0,0 @@
package charger
// Code generated by github.com/evcc-io/evcc/cmd/tools/decorate.go. DO NOT EDIT.
import (
"reflect"
"github.com/evcc-io/evcc/api"
)
func decoratePCE(base *PCElectric, meter func() (float64, error), meterEnergy func() (float64, error), phaseCurrents func() (float64, float64, float64, error)) api.Charger {
caps := make(map[reflect.Type]any)
if meter != nil {
caps[reflect.TypeFor[api.Meter]()] = &decoratePCEMeterImpl{meter: meter}
}
if meterEnergy != nil {
caps[reflect.TypeFor[api.MeterEnergy]()] = &decoratePCEMeterEnergyImpl{meterEnergy: meterEnergy}
}
if phaseCurrents != nil {
caps[reflect.TypeFor[api.PhaseCurrents]()] = &decoratePCEPhaseCurrentsImpl{phaseCurrents: phaseCurrents}
}
if len(caps) == 0 {
return base
}
return &decoratePCECapable{PCElectric: base, caps: caps}
}
type decoratePCECapable struct {
*PCElectric
caps map[reflect.Type]any
}
func (d *decoratePCECapable) Capability(typ reflect.Type) (any, bool) {
c, ok := d.caps[typ]
if !ok && reflect.TypeOf(d).Implements(typ) {
return d, true
}
return c, ok
}
type decoratePCEMeterImpl struct {
meter func() (float64, error)
}
func (impl *decoratePCEMeterImpl) CurrentPower() (float64, error) {
return impl.meter()
}
type decoratePCEMeterEnergyImpl struct {
meterEnergy func() (float64, error)
}
func (impl *decoratePCEMeterEnergyImpl) TotalEnergy() (float64, error) {
return impl.meterEnergy()
}
type decoratePCEPhaseCurrentsImpl struct {
phaseCurrents func() (float64, float64, float64, error)
}
func (impl *decoratePCEPhaseCurrentsImpl) Currents() (float64, float64, float64, error) {
return impl.phaseCurrents()
}

View file

@ -25,6 +25,7 @@ import (
"fmt"
"github.com/evcc-io/evcc/api"
"github.com/evcc-io/evcc/api/implement"
"github.com/evcc-io/evcc/util"
"github.com/evcc-io/evcc/util/modbus"
"github.com/evcc-io/evcc/util/sponsor"
@ -33,6 +34,7 @@ import (
// Peblar charger implementation
type Peblar struct {
implement.Caps
conn *modbus.Connection
curr uint32
enabled bool
@ -70,8 +72,6 @@ func init() {
registry.AddCtx("peblar", NewPeblarFromConfig)
}
//go:generate go tool decorate -f decoratePeblar -b *Peblar -r api.Charger -t api.PhaseSwitcher,api.PhaseGetter
// NewPeblarFromConfig creates a Peblar charger from generic config
func NewPeblarFromConfig(ctx context.Context, other map[string]any) (api.Charger, error) {
cc := modbus.TcpSettings{
@ -103,6 +103,7 @@ func NewPeblar(ctx context.Context, uri string, id uint8) (api.Charger, error) {
}
wb := &Peblar{
Caps: implement.New(),
conn: conn,
curr: 6000, // assume min current
phases: binary.BigEndian.Uint16(b), // required for retrieving the right amount of voltage/current registers
@ -113,15 +114,12 @@ func NewPeblar(ctx context.Context, uri string, id uint8) (api.Charger, error) {
return nil, err
}
var phasesS func(int) error
var phasesG func() (int, error)
if binary.BigEndian.Uint16(b) == 1 {
phasesS = wb.phases1p3p
phasesG = wb.getPhases
implement.Has(wb, implement.PhaseSwitcher(wb.phases1p3p))
implement.Has(wb, implement.PhaseGetter(wb.getPhases))
}
return decoratePeblar(wb, phasesS, phasesG), nil
return wb, nil
}
// Status implements the api.Charger interface

View file

@ -1,56 +0,0 @@
package charger
// Code generated by github.com/evcc-io/evcc/cmd/tools/decorate.go. DO NOT EDIT.
import (
"reflect"
"github.com/evcc-io/evcc/api"
)
func decoratePeblar(base *Peblar, phaseSwitcher func(int) error, phaseGetter func() (int, error)) api.Charger {
caps := make(map[reflect.Type]any)
if phaseSwitcher != nil {
caps[reflect.TypeFor[api.PhaseSwitcher]()] = &decoratePeblarPhaseSwitcherImpl{phaseSwitcher: phaseSwitcher}
}
if phaseGetter != nil {
caps[reflect.TypeFor[api.PhaseGetter]()] = &decoratePeblarPhaseGetterImpl{phaseGetter: phaseGetter}
}
if len(caps) == 0 {
return base
}
return &decoratePeblarCapable{Peblar: base, caps: caps}
}
type decoratePeblarCapable struct {
*Peblar
caps map[reflect.Type]any
}
func (d *decoratePeblarCapable) Capability(typ reflect.Type) (any, bool) {
c, ok := d.caps[typ]
if !ok && reflect.TypeOf(d).Implements(typ) {
return d, true
}
return c, ok
}
type decoratePeblarPhaseGetterImpl struct {
phaseGetter func() (int, error)
}
func (impl *decoratePeblarPhaseGetterImpl) GetPhases() (int, error) {
return impl.phaseGetter()
}
type decoratePeblarPhaseSwitcherImpl struct {
phaseSwitcher func(int) error
}
func (impl *decoratePeblarPhaseSwitcherImpl) Phases1p3p(p0 int) error {
return impl.phaseSwitcher(p0)
}

View file

@ -6,6 +6,7 @@ import (
"time"
"github.com/evcc-io/evcc/api"
"github.com/evcc-io/evcc/api/implement"
"github.com/evcc-io/evcc/util"
"github.com/evcc-io/evcc/util/modbus"
"github.com/volkszaehler/mbmd/encoding"
@ -38,6 +39,7 @@ const (
// PhoenixCharx is an api.Charger implementation for Phoenix CHARX controller
type PhoenixCharx struct {
implement.Caps
conn *modbus.Connection
connector uint16
current uint16
@ -47,8 +49,6 @@ func init() {
registry.AddCtx("phoenix-charx", NewPhoenixCharxFromConfig)
}
//go:generate go tool decorate -f decoratePhoenixCharx -b *PhoenixCharx -r api.Charger -t api.Meter,api.MeterEnergy,api.PhaseCurrents,api.PhaseVoltages
// NewPhoenixCharxFromConfig creates a Phoenix charger from generic config
func NewPhoenixCharxFromConfig(ctx context.Context, other map[string]any) (api.Charger, error) {
cc := struct {
@ -76,7 +76,10 @@ func NewPhoenixCharxFromConfig(ctx context.Context, other map[string]any) (api.C
}
if meter > 0 && meter != 65535 {
return decoratePhoenixCharx(wb, wb.currentPower, wb.totalEnergy, wb.currents, wb.voltages), nil
implement.Has(wb, implement.Meter(wb.currentPower))
implement.Has(wb, implement.MeterEnergy(wb.totalEnergy))
implement.Has(wb, implement.PhaseCurrents(wb.currents))
implement.Has(wb, implement.PhaseVoltages(wb.voltages))
}
return wb, nil
@ -93,6 +96,7 @@ func NewPhoenixCharx(ctx context.Context, uri string, id uint8, connector uint16
conn.Logger(log.TRACE)
wb := &PhoenixCharx{
Caps: implement.New(),
conn: conn,
connector: connector,
current: 6, // assume min current

View file

@ -1,80 +0,0 @@
package charger
// Code generated by github.com/evcc-io/evcc/cmd/tools/decorate.go. DO NOT EDIT.
import (
"reflect"
"github.com/evcc-io/evcc/api"
)
func decoratePhoenixCharx(base *PhoenixCharx, meter func() (float64, error), meterEnergy func() (float64, error), phaseCurrents func() (float64, float64, float64, error), phaseVoltages func() (float64, float64, float64, error)) api.Charger {
caps := make(map[reflect.Type]any)
if meter != nil {
caps[reflect.TypeFor[api.Meter]()] = &decoratePhoenixCharxMeterImpl{meter: meter}
}
if meterEnergy != nil {
caps[reflect.TypeFor[api.MeterEnergy]()] = &decoratePhoenixCharxMeterEnergyImpl{meterEnergy: meterEnergy}
}
if phaseCurrents != nil {
caps[reflect.TypeFor[api.PhaseCurrents]()] = &decoratePhoenixCharxPhaseCurrentsImpl{phaseCurrents: phaseCurrents}
}
if phaseVoltages != nil {
caps[reflect.TypeFor[api.PhaseVoltages]()] = &decoratePhoenixCharxPhaseVoltagesImpl{phaseVoltages: phaseVoltages}
}
if len(caps) == 0 {
return base
}
return &decoratePhoenixCharxCapable{PhoenixCharx: base, caps: caps}
}
type decoratePhoenixCharxCapable struct {
*PhoenixCharx
caps map[reflect.Type]any
}
func (d *decoratePhoenixCharxCapable) Capability(typ reflect.Type) (any, bool) {
c, ok := d.caps[typ]
if !ok && reflect.TypeOf(d).Implements(typ) {
return d, true
}
return c, ok
}
type decoratePhoenixCharxMeterImpl struct {
meter func() (float64, error)
}
func (impl *decoratePhoenixCharxMeterImpl) CurrentPower() (float64, error) {
return impl.meter()
}
type decoratePhoenixCharxMeterEnergyImpl struct {
meterEnergy func() (float64, error)
}
func (impl *decoratePhoenixCharxMeterEnergyImpl) TotalEnergy() (float64, error) {
return impl.meterEnergy()
}
type decoratePhoenixCharxPhaseCurrentsImpl struct {
phaseCurrents func() (float64, float64, float64, error)
}
func (impl *decoratePhoenixCharxPhaseCurrentsImpl) Currents() (float64, float64, float64, error) {
return impl.phaseCurrents()
}
type decoratePhoenixCharxPhaseVoltagesImpl struct {
phaseVoltages func() (float64, float64, float64, error)
}
func (impl *decoratePhoenixCharxPhaseVoltagesImpl) Voltages() (float64, float64, float64, error) {
return impl.phaseVoltages()
}

View file

@ -6,6 +6,7 @@ import (
"time"
"github.com/evcc-io/evcc/api"
"github.com/evcc-io/evcc/api/implement"
"github.com/evcc-io/evcc/util"
"github.com/evcc-io/evcc/util/modbus"
"github.com/volkszaehler/mbmd/encoding"
@ -27,6 +28,7 @@ const (
// PhoenixEMEth is an api.Charger implementation for Phoenix EM-CP-PP-ETH wallboxes.
// It uses Modbus TCP to communicate with the wallbox at modbus client id 180.
type PhoenixEMEth struct {
implement.Caps
conn *modbus.Connection
}
@ -34,8 +36,6 @@ func init() {
registry.AddCtx("phoenix-em-eth", NewPhoenixEMEthFromConfig)
}
//go:generate go tool decorate -f decoratePhoenixEMEth -b *PhoenixEMEth -r api.Charger -t api.Meter,api.MeterEnergy,api.PhaseCurrents,api.PhaseVoltages
// NewPhoenixEMEthFromConfig creates a Phoenix charger from generic config
func NewPhoenixEMEthFromConfig(ctx context.Context, other map[string]any) (api.Charger, error) {
cc := modbus.TcpSettings{
@ -51,22 +51,15 @@ func NewPhoenixEMEthFromConfig(ctx context.Context, other map[string]any) (api.C
return nil, err
}
var (
currentPower func() (float64, error)
totalEnergy func() (float64, error)
currents func() (float64, float64, float64, error)
voltages func() (float64, float64, float64, error)
)
// check presence of meter by voltage on l1
if b, err := wb.conn.ReadInputRegisters(phxEMEthRegVoltages, 2); err == nil && encoding.Int32LswFirst(b) > 0 {
currentPower = wb.currentPower
totalEnergy = wb.totalEnergy
currents = wb.currents
voltages = wb.voltages
implement.Has(wb, implement.Meter(wb.currentPower))
implement.Has(wb, implement.MeterEnergy(wb.totalEnergy))
implement.Has(wb, implement.PhaseCurrents(wb.currents))
implement.Has(wb, implement.PhaseVoltages(wb.voltages))
}
return decoratePhoenixEMEth(wb, currentPower, totalEnergy, currents, voltages), nil
return wb, nil
}
// NewPhoenixEMEth creates a Phoenix charger
@ -80,6 +73,7 @@ func NewPhoenixEMEth(ctx context.Context, uri string, slaveID uint8) (*PhoenixEM
conn.Logger(log.TRACE)
wb := &PhoenixEMEth{
Caps: implement.New(),
conn: conn,
}

View file

@ -1,80 +0,0 @@
package charger
// Code generated by github.com/evcc-io/evcc/cmd/tools/decorate.go. DO NOT EDIT.
import (
"reflect"
"github.com/evcc-io/evcc/api"
)
func decoratePhoenixEMEth(base *PhoenixEMEth, meter func() (float64, error), meterEnergy func() (float64, error), phaseCurrents func() (float64, float64, float64, error), phaseVoltages func() (float64, float64, float64, error)) api.Charger {
caps := make(map[reflect.Type]any)
if meter != nil {
caps[reflect.TypeFor[api.Meter]()] = &decoratePhoenixEMEthMeterImpl{meter: meter}
}
if meterEnergy != nil {
caps[reflect.TypeFor[api.MeterEnergy]()] = &decoratePhoenixEMEthMeterEnergyImpl{meterEnergy: meterEnergy}
}
if phaseCurrents != nil {
caps[reflect.TypeFor[api.PhaseCurrents]()] = &decoratePhoenixEMEthPhaseCurrentsImpl{phaseCurrents: phaseCurrents}
}
if phaseVoltages != nil {
caps[reflect.TypeFor[api.PhaseVoltages]()] = &decoratePhoenixEMEthPhaseVoltagesImpl{phaseVoltages: phaseVoltages}
}
if len(caps) == 0 {
return base
}
return &decoratePhoenixEMEthCapable{PhoenixEMEth: base, caps: caps}
}
type decoratePhoenixEMEthCapable struct {
*PhoenixEMEth
caps map[reflect.Type]any
}
func (d *decoratePhoenixEMEthCapable) Capability(typ reflect.Type) (any, bool) {
c, ok := d.caps[typ]
if !ok && reflect.TypeOf(d).Implements(typ) {
return d, true
}
return c, ok
}
type decoratePhoenixEMEthMeterImpl struct {
meter func() (float64, error)
}
func (impl *decoratePhoenixEMEthMeterImpl) CurrentPower() (float64, error) {
return impl.meter()
}
type decoratePhoenixEMEthMeterEnergyImpl struct {
meterEnergy func() (float64, error)
}
func (impl *decoratePhoenixEMEthMeterEnergyImpl) TotalEnergy() (float64, error) {
return impl.meterEnergy()
}
type decoratePhoenixEMEthPhaseCurrentsImpl struct {
phaseCurrents func() (float64, float64, float64, error)
}
func (impl *decoratePhoenixEMEthPhaseCurrentsImpl) Currents() (float64, float64, float64, error) {
return impl.phaseCurrents()
}
type decoratePhoenixEMEthPhaseVoltagesImpl struct {
phaseVoltages func() (float64, float64, float64, error)
}
func (impl *decoratePhoenixEMEthPhaseVoltagesImpl) Voltages() (float64, float64, float64, error) {
return impl.phaseVoltages()
}

View file

@ -28,6 +28,7 @@ import (
"fmt"
"github.com/evcc-io/evcc/api"
"github.com/evcc-io/evcc/api/implement"
"github.com/evcc-io/evcc/util"
"github.com/evcc-io/evcc/util/modbus"
"github.com/evcc-io/evcc/util/sponsor"
@ -35,6 +36,7 @@ import (
)
type PhoenixEVEth struct {
implement.Caps
conn *modbus.Connection
isWallbe bool
}
@ -62,8 +64,6 @@ func init() {
registry.AddCtx("phoenix-ev-eth", NewPhoenixEVEthFromConfig)
}
//go:generate go tool decorate -f decoratePhoenixEVEth -b *PhoenixEVEth -r api.Charger -t api.Meter,api.MeterEnergy,api.PhaseCurrents,api.PhaseVoltages,api.ChargerEx,api.Identifier
// NewPhoenixEVEthFromConfig creates a PhoenixEVEth charger from generic config
func NewPhoenixEVEthFromConfig(ctx context.Context, other map[string]any) (api.Charger, error) {
cc := modbus.TcpSettings{
@ -92,38 +92,30 @@ func NewPhoenixEVEth(ctx context.Context, uri string, slaveID uint8) (api.Charge
conn.Logger(log.TRACE)
wb := &PhoenixEVEth{
Caps: implement.New(),
conn: conn,
}
var (
currentPower func() (float64, error)
totalEnergy func() (float64, error)
currents func() (float64, float64, float64, error)
voltages func() (float64, float64, float64, error)
maxCurrentMillis func(float64) error
identify func() (string, error)
)
// check presence of meter by voltage on l1
if b, err := wb.conn.ReadInputRegisters(phxRegVoltages, 2); err == nil && encoding.Uint32LswFirst(b) > 0 {
currentPower = wb.currentPower
totalEnergy = wb.totalEnergy
currents = wb.currents
voltages = wb.voltages
implement.May(wb, implement.Meter(wb.currentPower))
implement.May(wb, implement.MeterEnergy(wb.totalEnergy))
implement.May(wb, implement.PhaseCurrents(wb.currents))
implement.May(wb, implement.PhaseVoltages(wb.voltages))
}
// check card reader enabled
if b, err := wb.conn.ReadCoils(phxRegCardEnabled, 1); err == nil && b[0] == 1 {
identify = wb.identify
implement.May(wb, implement.Identifier(wb.identify))
}
// check presence of extended Wallbe firmware
if b, err := wb.conn.ReadHoldingRegisters(phxRegMaxCurrent, 1); err == nil && encoding.Uint16(b) >= 60 {
wb.isWallbe = true
maxCurrentMillis = wb.maxCurrentMillis
implement.May(wb, implement.ChargerEx(wb.maxCurrentMillis))
}
return decoratePhoenixEVEth(wb, currentPower, totalEnergy, currents, voltages, maxCurrentMillis, identify), nil
return wb, nil
}
// Status implements the api.Charger interface

View file

@ -1,104 +0,0 @@
package charger
// Code generated by github.com/evcc-io/evcc/cmd/tools/decorate.go. DO NOT EDIT.
import (
"reflect"
"github.com/evcc-io/evcc/api"
)
func decoratePhoenixEVEth(base *PhoenixEVEth, meter func() (float64, error), meterEnergy func() (float64, error), phaseCurrents func() (float64, float64, float64, error), phaseVoltages func() (float64, float64, float64, error), chargerEx func(float64) error, identifier func() (string, error)) api.Charger {
caps := make(map[reflect.Type]any)
if meter != nil {
caps[reflect.TypeFor[api.Meter]()] = &decoratePhoenixEVEthMeterImpl{meter: meter}
}
if meterEnergy != nil {
caps[reflect.TypeFor[api.MeterEnergy]()] = &decoratePhoenixEVEthMeterEnergyImpl{meterEnergy: meterEnergy}
}
if phaseCurrents != nil {
caps[reflect.TypeFor[api.PhaseCurrents]()] = &decoratePhoenixEVEthPhaseCurrentsImpl{phaseCurrents: phaseCurrents}
}
if phaseVoltages != nil {
caps[reflect.TypeFor[api.PhaseVoltages]()] = &decoratePhoenixEVEthPhaseVoltagesImpl{phaseVoltages: phaseVoltages}
}
if chargerEx != nil {
caps[reflect.TypeFor[api.ChargerEx]()] = &decoratePhoenixEVEthChargerExImpl{chargerEx: chargerEx}
}
if identifier != nil {
caps[reflect.TypeFor[api.Identifier]()] = &decoratePhoenixEVEthIdentifierImpl{identifier: identifier}
}
if len(caps) == 0 {
return base
}
return &decoratePhoenixEVEthCapable{PhoenixEVEth: base, caps: caps}
}
type decoratePhoenixEVEthCapable struct {
*PhoenixEVEth
caps map[reflect.Type]any
}
func (d *decoratePhoenixEVEthCapable) Capability(typ reflect.Type) (any, bool) {
c, ok := d.caps[typ]
if !ok && reflect.TypeOf(d).Implements(typ) {
return d, true
}
return c, ok
}
type decoratePhoenixEVEthChargerExImpl struct {
chargerEx func(float64) error
}
func (impl *decoratePhoenixEVEthChargerExImpl) MaxCurrentMillis(p0 float64) error {
return impl.chargerEx(p0)
}
type decoratePhoenixEVEthIdentifierImpl struct {
identifier func() (string, error)
}
func (impl *decoratePhoenixEVEthIdentifierImpl) Identify() (string, error) {
return impl.identifier()
}
type decoratePhoenixEVEthMeterImpl struct {
meter func() (float64, error)
}
func (impl *decoratePhoenixEVEthMeterImpl) CurrentPower() (float64, error) {
return impl.meter()
}
type decoratePhoenixEVEthMeterEnergyImpl struct {
meterEnergy func() (float64, error)
}
func (impl *decoratePhoenixEVEthMeterEnergyImpl) TotalEnergy() (float64, error) {
return impl.meterEnergy()
}
type decoratePhoenixEVEthPhaseCurrentsImpl struct {
phaseCurrents func() (float64, float64, float64, error)
}
func (impl *decoratePhoenixEVEthPhaseCurrentsImpl) Currents() (float64, float64, float64, error) {
return impl.phaseCurrents()
}
type decoratePhoenixEVEthPhaseVoltagesImpl struct {
phaseVoltages func() (float64, float64, float64, error)
}
func (impl *decoratePhoenixEVEthPhaseVoltagesImpl) Voltages() (float64, float64, float64, error) {
return impl.phaseVoltages()
}

View file

@ -24,12 +24,14 @@ import (
"time"
"github.com/evcc-io/evcc/api"
"github.com/evcc-io/evcc/api/implement"
"github.com/evcc-io/evcc/util"
"github.com/evcc-io/evcc/util/modbus"
)
// Pulsares charger implementation
type Pulsares struct {
implement.Caps
log *util.Logger
conn *modbus.Connection
curr uint16
@ -51,8 +53,6 @@ func init() {
registry.AddCtx("pulsares", NewPulsaresFromConfig)
}
//go:generate go tool decorate -f decoratePulsares -b *Pulsares -r api.Charger -t api.PhaseSwitcher
// NewPulsaresFromConfig creates a Pulsares charger from generic config
func NewPulsaresFromConfig(ctx context.Context, other map[string]any) (api.Charger, error) {
cc := modbus.Settings{
@ -68,12 +68,11 @@ func NewPulsaresFromConfig(ctx context.Context, other map[string]any) (api.Charg
return nil, err
}
var phases1p3p func(int) error
if wb.has1p3p() {
phases1p3p = wb.phases1p3p
implement.Has(wb, implement.PhaseSwitcher(wb.phases1p3p))
}
return decoratePulsares(wb, phases1p3p), nil
return wb, nil
}
// NewPulsares creates Pulsares charger
@ -87,6 +86,7 @@ func NewPulsares(ctx context.Context, uri, device, comset string, baudrate int,
conn.Logger(log.TRACE)
wb := &Pulsares{
Caps: implement.New(),
conn: conn,
curr: 6000,
}

View file

@ -1,44 +0,0 @@
package charger
// Code generated by github.com/evcc-io/evcc/cmd/tools/decorate.go. DO NOT EDIT.
import (
"reflect"
"github.com/evcc-io/evcc/api"
)
func decoratePulsares(base *Pulsares, phaseSwitcher func(int) error) api.Charger {
caps := make(map[reflect.Type]any)
if phaseSwitcher != nil {
caps[reflect.TypeFor[api.PhaseSwitcher]()] = &decoratePulsaresPhaseSwitcherImpl{phaseSwitcher: phaseSwitcher}
}
if len(caps) == 0 {
return base
}
return &decoratePulsaresCapable{Pulsares: base, caps: caps}
}
type decoratePulsaresCapable struct {
*Pulsares
caps map[reflect.Type]any
}
func (d *decoratePulsaresCapable) Capability(typ reflect.Type) (any, bool) {
c, ok := d.caps[typ]
if !ok && reflect.TypeOf(d).Implements(typ) {
return d, true
}
return c, ok
}
type decoratePulsaresPhaseSwitcherImpl struct {
phaseSwitcher func(int) error
}
func (impl *decoratePulsaresPhaseSwitcherImpl) Phases1p3p(p0 int) error {
return impl.phaseSwitcher(p0)
}

View file

@ -23,6 +23,7 @@ import (
"time"
"github.com/evcc-io/evcc/api"
"github.com/evcc-io/evcc/api/implement"
"github.com/evcc-io/evcc/charger/semp"
"github.com/evcc-io/evcc/util"
"github.com/evcc-io/evcc/util/sponsor"
@ -30,6 +31,7 @@ import (
// SEMP charger implementation
type SEMP struct {
implement.Caps
log *util.Logger
conn *semp.Connection
cache time.Duration
@ -44,8 +46,6 @@ type SEMP struct {
hasStatusParam bool
}
//go:generate go tool decorate -f decorateSEMP -b *SEMP -r api.Charger -t api.PhaseSwitcher,api.PhaseGetter,api.ChargeRater
func init() {
registry.AddCtx("semp", NewSEMPFromConfig)
}
@ -76,6 +76,7 @@ func NewSEMP(ctx context.Context, uri, deviceID string, cache time.Duration) (ap
log := util.NewLogger("semp")
wb := &SEMP{
Caps: implement.New(),
log: log,
cache: cache,
phases: 3,
@ -113,12 +114,6 @@ func NewSEMP(ctx context.Context, uri, deviceID string, cache time.Duration) (ap
log.DEBUG.Printf("auto-detected device ID: %s", wb.deviceID)
}
var (
phases1p3p func(int) error
getPhases func() (int, error)
chargedEnergy func() (float64, error)
)
// Check if device supports phase switching by checking power characteristics
info, err := wb.getDeviceInfo()
if err != nil {
@ -130,13 +125,13 @@ func NewSEMP(ctx context.Context, uri, deviceID string, cache time.Duration) (ap
// Assume Phase switching support if MinPowerConsumption < 4140W and MaxPowerConsumption > 4600W
if wb.minPower > 0 && wb.minPower < 4140 && wb.maxPower > 4600 {
phases1p3p = wb.phases1p3p
getPhases = wb.getPhases
implement.Has(wb, implement.PhaseSwitcher(wb.phases1p3p))
implement.Has(wb, implement.PhaseGetter(wb.getPhases))
}
// Check if device supports charged energy reporting via Parameters endpoint
if _, err := wb.getParameter("Measurement.ChaSess.WhIn"); err == nil {
chargedEnergy = wb.chargedEnergy
implement.Has(wb, implement.ChargeRater(wb.chargedEnergy))
}
// Check if device supports status reporting via Parameters endpoint
@ -151,7 +146,7 @@ func NewSEMP(ctx context.Context, uri, deviceID string, cache time.Duration) (ap
go wb.heartbeat(ctx)
return decorateSEMP(wb, phases1p3p, getPhases, chargedEnergy), nil
return wb, nil
}
// heartbeat ensures that device control updates are sent at least once per minute

View file

@ -1,68 +0,0 @@
package charger
// Code generated by github.com/evcc-io/evcc/cmd/tools/decorate.go. DO NOT EDIT.
import (
"reflect"
"github.com/evcc-io/evcc/api"
)
func decorateSEMP(base *SEMP, phaseSwitcher func(int) error, phaseGetter func() (int, error), chargeRater func() (float64, error)) api.Charger {
caps := make(map[reflect.Type]any)
if phaseSwitcher != nil {
caps[reflect.TypeFor[api.PhaseSwitcher]()] = &decorateSEMPPhaseSwitcherImpl{phaseSwitcher: phaseSwitcher}
}
if phaseGetter != nil {
caps[reflect.TypeFor[api.PhaseGetter]()] = &decorateSEMPPhaseGetterImpl{phaseGetter: phaseGetter}
}
if chargeRater != nil {
caps[reflect.TypeFor[api.ChargeRater]()] = &decorateSEMPChargeRaterImpl{chargeRater: chargeRater}
}
if len(caps) == 0 {
return base
}
return &decorateSEMPCapable{SEMP: base, caps: caps}
}
type decorateSEMPCapable struct {
*SEMP
caps map[reflect.Type]any
}
func (d *decorateSEMPCapable) Capability(typ reflect.Type) (any, bool) {
c, ok := d.caps[typ]
if !ok && reflect.TypeOf(d).Implements(typ) {
return d, true
}
return c, ok
}
type decorateSEMPChargeRaterImpl struct {
chargeRater func() (float64, error)
}
func (impl *decorateSEMPChargeRaterImpl) ChargedEnergy() (float64, error) {
return impl.chargeRater()
}
type decorateSEMPPhaseGetterImpl struct {
phaseGetter func() (int, error)
}
func (impl *decorateSEMPPhaseGetterImpl) GetPhases() (int, error) {
return impl.phaseGetter()
}
type decorateSEMPPhaseSwitcherImpl struct {
phaseSwitcher func(int) error
}
func (impl *decorateSEMPPhaseSwitcherImpl) Phases1p3p(p0 int) error {
return impl.phaseSwitcher(p0)
}

View file

@ -5,6 +5,7 @@ import (
"fmt"
"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/util"
"github.com/evcc-io/evcc/util/config"
@ -55,11 +56,6 @@ func NewSgReadyRelayFromConfig(ctx context.Context, other map[string]any) (api.C
}
}
tempG, limitTempG, err := cc.Temperature.Configure(ctx)
if err != nil {
return nil, err
}
res, err := NewSgReadyRelay(ctx, &cc.embed, boost, dim)
if err != nil {
return nil, err
@ -69,8 +65,17 @@ func NewSgReadyRelayFromConfig(ctx context.Context, other map[string]any) (api.C
if err != nil {
return nil, err
}
implement.May(res, implement.Meter(powerG))
implement.May(res, implement.MeterEnergy(energyG))
return decorateSgReady(res, powerG, energyG, tempG, limitTempG), nil
tempG, limitTempG, err := cc.Temperature.Configure(ctx)
if err != nil {
return nil, err
}
implement.May(res, implement.Battery(tempG))
implement.May(res, implement.SocLimiter(limitTempG))
return res, nil
}
// NewSgReadyRelay creates SG Ready charger with boost relay

View file

@ -21,6 +21,7 @@ import (
"context"
"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"
@ -30,6 +31,7 @@ import (
// SgReady charger implementation
type SgReady struct {
*embed
implement.Caps
mode int64
modeS func(int64) error
modeG func() (int64, error)
@ -51,8 +53,6 @@ const (
Boost // 3
)
//go:generate go tool decorate -f decorateSgReady -b *SgReady -r api.Charger -t api.Meter,api.MeterEnergy,api.Battery,api.SocLimiter
// NewSgReadyFromConfig creates an SG Ready configurable charger from generic config
func NewSgReadyFromConfig(ctx context.Context, other map[string]any) (api.Charger, error) {
cc := struct {
@ -111,19 +111,24 @@ func NewSgReadyFromConfig(ctx context.Context, other map[string]any) (api.Charge
if err != nil {
return nil, err
}
implement.May(res, implement.Meter(powerG))
implement.May(res, implement.MeterEnergy(energyG))
tempG, limitTempG, err := cc.Temperature.Configure(ctx)
if err != nil {
return nil, err
}
implement.May(res, implement.Battery(tempG))
implement.May(res, implement.SocLimiter(limitTempG))
return decorateSgReady(res, powerG, energyG, tempG, limitTempG), nil
return res, nil
}
// NewSgReady creates SG Ready charger
func NewSgReady(ctx context.Context, embed *embed, modeS func(int64) error, modeG func() (int64, error), maxPowerS func(int64) error) (*SgReady, error) {
res := &SgReady{
embed: embed,
Caps: implement.New(),
mode: Normal,
modeS: modeS,
modeG: modeG,

View file

@ -1,80 +0,0 @@
package charger
// Code generated by github.com/evcc-io/evcc/cmd/tools/decorate.go. DO NOT EDIT.
import (
"reflect"
"github.com/evcc-io/evcc/api"
)
func decorateSgReady(base *SgReady, meter func() (float64, error), meterEnergy func() (float64, error), battery func() (float64, error), socLimiter func() (int64, error)) api.Charger {
caps := make(map[reflect.Type]any)
if meter != nil {
caps[reflect.TypeFor[api.Meter]()] = &decorateSgReadyMeterImpl{meter: meter}
}
if meterEnergy != nil {
caps[reflect.TypeFor[api.MeterEnergy]()] = &decorateSgReadyMeterEnergyImpl{meterEnergy: meterEnergy}
}
if battery != nil {
caps[reflect.TypeFor[api.Battery]()] = &decorateSgReadyBatteryImpl{battery: battery}
}
if socLimiter != nil {
caps[reflect.TypeFor[api.SocLimiter]()] = &decorateSgReadySocLimiterImpl{socLimiter: socLimiter}
}
if len(caps) == 0 {
return base
}
return &decorateSgReadyCapable{SgReady: base, caps: caps}
}
type decorateSgReadyCapable struct {
*SgReady
caps map[reflect.Type]any
}
func (d *decorateSgReadyCapable) Capability(typ reflect.Type) (any, bool) {
c, ok := d.caps[typ]
if !ok && reflect.TypeOf(d).Implements(typ) {
return d, true
}
return c, ok
}
type decorateSgReadyBatteryImpl struct {
battery func() (float64, error)
}
func (impl *decorateSgReadyBatteryImpl) Soc() (float64, error) {
return impl.battery()
}
type decorateSgReadyMeterImpl struct {
meter func() (float64, error)
}
func (impl *decorateSgReadyMeterImpl) CurrentPower() (float64, error) {
return impl.meter()
}
type decorateSgReadyMeterEnergyImpl struct {
meterEnergy func() (float64, error)
}
func (impl *decorateSgReadyMeterEnergyImpl) TotalEnergy() (float64, error) {
return impl.meterEnergy()
}
type decorateSgReadySocLimiterImpl struct {
socLimiter func() (int64, error)
}
func (impl *decorateSgReadySocLimiterImpl) GetLimitSoc() (int64, error) {
return impl.socLimiter()
}

View file

@ -5,12 +5,14 @@ import (
"time"
"github.com/evcc-io/evcc/api"
"github.com/evcc-io/evcc/api/implement"
"github.com/evcc-io/evcc/meter/shelly"
"github.com/evcc-io/evcc/util"
)
// Shelly charger implementation
type Shelly struct {
implement.Caps
conn *shelly.Connection
*switchSocket
}
@ -19,8 +21,6 @@ func init() {
registry.Add("shelly", NewShellyFromConfig)
}
//go:generate go tool decorate -f decorateShelly -b *Shelly -r api.Charger -t api.PhaseVoltages,api.PhaseCurrents,api.PhasePowers
// NewShellyFromConfig creates a Shelly charger from generic config
func NewShellyFromConfig(other map[string]any) (api.Charger, error) {
cc := struct {
@ -44,14 +44,13 @@ func NewShellyFromConfig(other map[string]any) (api.Charger, error) {
return nil, err
}
var vol, cur, pow func() (float64, float64, float64, error)
if phases, ok := c.conn.Generation.(shelly.Phases); ok {
vol = phases.Voltages
cur = phases.Currents
pow = phases.Powers
implement.Has(c, implement.PhaseVoltages(phases.Voltages))
implement.Has(c, implement.PhaseCurrents(phases.Currents))
implement.Has(c, implement.PhasePowers(phases.Powers))
}
return decorateShelly(c, vol, cur, pow), nil
return c, nil
}
// NewShelly creates Shelly charger
@ -62,6 +61,7 @@ func NewShelly(embed embed, uri, user, password string, channel int, standbypowe
}
c := &Shelly{
Caps: implement.New(),
conn: conn,
}

View file

@ -1,68 +0,0 @@
package charger
// Code generated by github.com/evcc-io/evcc/cmd/tools/decorate.go. DO NOT EDIT.
import (
"reflect"
"github.com/evcc-io/evcc/api"
)
func decorateShelly(base *Shelly, phaseVoltages func() (float64, float64, float64, error), phaseCurrents func() (float64, float64, float64, error), phasePowers func() (float64, float64, float64, error)) api.Charger {
caps := make(map[reflect.Type]any)
if phaseVoltages != nil {
caps[reflect.TypeFor[api.PhaseVoltages]()] = &decorateShellyPhaseVoltagesImpl{phaseVoltages: phaseVoltages}
}
if phaseCurrents != nil {
caps[reflect.TypeFor[api.PhaseCurrents]()] = &decorateShellyPhaseCurrentsImpl{phaseCurrents: phaseCurrents}
}
if phasePowers != nil {
caps[reflect.TypeFor[api.PhasePowers]()] = &decorateShellyPhasePowersImpl{phasePowers: phasePowers}
}
if len(caps) == 0 {
return base
}
return &decorateShellyCapable{Shelly: base, caps: caps}
}
type decorateShellyCapable struct {
*Shelly
caps map[reflect.Type]any
}
func (d *decorateShellyCapable) Capability(typ reflect.Type) (any, bool) {
c, ok := d.caps[typ]
if !ok && reflect.TypeOf(d).Implements(typ) {
return d, true
}
return c, ok
}
type decorateShellyPhaseCurrentsImpl struct {
phaseCurrents func() (float64, float64, float64, error)
}
func (impl *decorateShellyPhaseCurrentsImpl) Currents() (float64, float64, float64, error) {
return impl.phaseCurrents()
}
type decorateShellyPhasePowersImpl struct {
phasePowers func() (float64, float64, float64, error)
}
func (impl *decorateShellyPhasePowersImpl) Powers() (float64, float64, float64, error) {
return impl.phasePowers()
}
type decorateShellyPhaseVoltagesImpl struct {
phaseVoltages func() (float64, float64, float64, error)
}
func (impl *decorateShellyPhaseVoltagesImpl) Voltages() (float64, float64, float64, error) {
return impl.phaseVoltages()
}

View file

@ -24,6 +24,7 @@ import (
"time"
"github.com/evcc-io/evcc/api"
"github.com/evcc-io/evcc/api/implement"
"github.com/evcc-io/evcc/util"
"github.com/evcc-io/evcc/util/request"
)
@ -34,6 +35,7 @@ import (
// SmartEVSE3 charger implementation
type SmartEVSE3 struct {
*request.Helper
implement.Caps
uri string
curr int64
mode int
@ -106,8 +108,6 @@ func init() {
registry.Add("smart-evse", NewSmartEVSE3FromConfig)
}
//go:generate go tool decorate -f decorateSmartEVSE3 -b *SmartEVSE3 -r api.Charger -t api.Meter,api.MeterEnergy,api.PhaseCurrents,api.PhaseSwitcher,api.PhaseGetter,api.Identifier,api.StatusReasoner
// NewSmartEVSE3FromConfig creates a SmartEVSE-3.5 REST charger from generic config
func NewSmartEVSE3FromConfig(other map[string]any) (api.Charger, error) {
cc := struct {
@ -141,6 +141,7 @@ func NewSmartEVSE3(uri string, cache time.Duration, mode int) (api.Charger, erro
wb := &SmartEVSE3{
Helper: request.NewHelper(log),
Caps: implement.New(),
uri: strings.TrimRight(util.DefaultScheme(uri, "http"), "/"),
curr: 60, // 6 A in 1/10 A
mode: mode,
@ -163,31 +164,25 @@ func NewSmartEVSE3(uri string, cache time.Duration, mode int) (api.Charger, erro
}
// decorate optional EV meter if configured in SmartEVSE
var currentPower, totalEnergy func() (float64, error)
var currents func() (float64, float64, float64, error)
if res.EvMeter.Description != "" && res.EvMeter.Description != "Disabled" {
currentPower = wb.currentPower
totalEnergy = wb.totalEnergy
currents = wb.currents
implement.Has(wb, implement.Meter(wb.currentPower))
implement.Has(wb, implement.MeterEnergy(wb.totalEnergy))
implement.Has(wb, implement.PhaseCurrents(wb.currents))
}
// decorate optional 1P/3P phase switching via C2 contactor
var phases1p3p func(int) error
var getPhases func() (int, error)
if res.Settings.EnableC2 != "" && res.Settings.EnableC2 != "Not present" {
phases1p3p = wb.phases1p3p
getPhases = wb.getPhases
implement.Has(wb, implement.PhaseSwitcher(wb.phases1p3p))
implement.Has(wb, implement.PhaseGetter(wb.getPhases))
}
// decorate optional RFID identification and status reason
var identify func() (string, error)
var statusReason func() (api.Reason, error)
if res.Evse.RFIDReader != "" && res.Evse.RFIDReader != "Disabled" {
identify = wb.identify
statusReason = wb.statusReason
implement.Has(wb, implement.Identifier(wb.identify))
implement.Has(wb, implement.StatusReasoner(wb.statusReason))
}
return decorateSmartEVSE3(wb, currentPower, totalEnergy, currents, phases1p3p, getPhases, identify, statusReason), nil
return wb, nil
}
// post issues a POST request to the SmartEVSE with given query parameters.

View file

@ -1,116 +0,0 @@
package charger
// Code generated by github.com/evcc-io/evcc/cmd/tools/decorate.go. DO NOT EDIT.
import (
"reflect"
"github.com/evcc-io/evcc/api"
)
func decorateSmartEVSE3(base *SmartEVSE3, meter func() (float64, error), meterEnergy func() (float64, error), phaseCurrents func() (float64, float64, float64, error), phaseSwitcher func(int) error, phaseGetter func() (int, error), identifier func() (string, error), statusReasoner func() (api.Reason, error)) api.Charger {
caps := make(map[reflect.Type]any)
if meter != nil {
caps[reflect.TypeFor[api.Meter]()] = &decorateSmartEVSE3MeterImpl{meter: meter}
}
if meterEnergy != nil {
caps[reflect.TypeFor[api.MeterEnergy]()] = &decorateSmartEVSE3MeterEnergyImpl{meterEnergy: meterEnergy}
}
if phaseCurrents != nil {
caps[reflect.TypeFor[api.PhaseCurrents]()] = &decorateSmartEVSE3PhaseCurrentsImpl{phaseCurrents: phaseCurrents}
}
if phaseSwitcher != nil {
caps[reflect.TypeFor[api.PhaseSwitcher]()] = &decorateSmartEVSE3PhaseSwitcherImpl{phaseSwitcher: phaseSwitcher}
}
if phaseGetter != nil {
caps[reflect.TypeFor[api.PhaseGetter]()] = &decorateSmartEVSE3PhaseGetterImpl{phaseGetter: phaseGetter}
}
if identifier != nil {
caps[reflect.TypeFor[api.Identifier]()] = &decorateSmartEVSE3IdentifierImpl{identifier: identifier}
}
if statusReasoner != nil {
caps[reflect.TypeFor[api.StatusReasoner]()] = &decorateSmartEVSE3StatusReasonerImpl{statusReasoner: statusReasoner}
}
if len(caps) == 0 {
return base
}
return &decorateSmartEVSE3Capable{SmartEVSE3: base, caps: caps}
}
type decorateSmartEVSE3Capable struct {
*SmartEVSE3
caps map[reflect.Type]any
}
func (d *decorateSmartEVSE3Capable) Capability(typ reflect.Type) (any, bool) {
c, ok := d.caps[typ]
if !ok && reflect.TypeOf(d).Implements(typ) {
return d, true
}
return c, ok
}
type decorateSmartEVSE3IdentifierImpl struct {
identifier func() (string, error)
}
func (impl *decorateSmartEVSE3IdentifierImpl) Identify() (string, error) {
return impl.identifier()
}
type decorateSmartEVSE3MeterImpl struct {
meter func() (float64, error)
}
func (impl *decorateSmartEVSE3MeterImpl) CurrentPower() (float64, error) {
return impl.meter()
}
type decorateSmartEVSE3MeterEnergyImpl struct {
meterEnergy func() (float64, error)
}
func (impl *decorateSmartEVSE3MeterEnergyImpl) TotalEnergy() (float64, error) {
return impl.meterEnergy()
}
type decorateSmartEVSE3PhaseCurrentsImpl struct {
phaseCurrents func() (float64, float64, float64, error)
}
func (impl *decorateSmartEVSE3PhaseCurrentsImpl) Currents() (float64, float64, float64, error) {
return impl.phaseCurrents()
}
type decorateSmartEVSE3PhaseGetterImpl struct {
phaseGetter func() (int, error)
}
func (impl *decorateSmartEVSE3PhaseGetterImpl) GetPhases() (int, error) {
return impl.phaseGetter()
}
type decorateSmartEVSE3PhaseSwitcherImpl struct {
phaseSwitcher func(int) error
}
func (impl *decorateSmartEVSE3PhaseSwitcherImpl) Phases1p3p(p0 int) error {
return impl.phaseSwitcher(p0)
}
type decorateSmartEVSE3StatusReasonerImpl struct {
statusReasoner func() (api.Reason, error)
}
func (impl *decorateSmartEVSE3StatusReasonerImpl) StatusReason() (api.Reason, error) {
return impl.statusReasoner()
}

View file

@ -24,6 +24,7 @@ import (
"strings"
"github.com/evcc-io/evcc/api"
"github.com/evcc-io/evcc/api/implement"
"github.com/evcc-io/evcc/util"
"github.com/evcc-io/evcc/util/modbus"
"github.com/evcc-io/evcc/util/sponsor"
@ -32,6 +33,7 @@ import (
// Solax charger implementation
type Solax struct {
implement.Caps
log *util.Logger
conn *modbus.Connection
isLegacyHw bool
@ -75,8 +77,6 @@ func init() {
registry.AddCtx("solax-g2", NewSolaxG2FromConfig)
}
//go:generate go tool decorate -f decorateSolax -b *Solax -r api.Charger -t api.PhaseSwitcher,api.PhaseGetter
func NewSolaxG1FromConfig(ctx context.Context, other map[string]any) (api.Charger, error) {
return NewSolaxFromConfig(ctx, other, true)
}
@ -113,23 +113,21 @@ func NewSolax(ctx context.Context, uri, device, comset string, baudrate int, pro
conn.Logger(log.TRACE)
wb := &Solax{
Caps: implement.New(),
log: log,
conn: conn,
isLegacyHw: isLegacyHw,
}
var phases1p3p func(int) error
var phasesG func() (int, error)
if b, err := wb.conn.ReadInputRegisters(solaxRegFirmwareVersion, 1); err == nil {
v := encoding.Uint16(b)
if !wb.isLegacyHw && v >= solaxFirmwarePhaseSwitching {
phases1p3p = wb.phases1p3p
phasesG = wb.getPhases
implement.Has(wb, implement.PhaseSwitcher(wb.phases1p3p))
implement.Has(wb, implement.PhaseGetter(wb.getPhases))
}
}
return decorateSolax(wb, phases1p3p, phasesG), nil
return wb, nil
}
// getPhaseValues returns 3 sequential register values

View file

@ -1,56 +0,0 @@
package charger
// Code generated by github.com/evcc-io/evcc/cmd/tools/decorate.go. DO NOT EDIT.
import (
"reflect"
"github.com/evcc-io/evcc/api"
)
func decorateSolax(base *Solax, phaseSwitcher func(int) error, phaseGetter func() (int, error)) api.Charger {
caps := make(map[reflect.Type]any)
if phaseSwitcher != nil {
caps[reflect.TypeFor[api.PhaseSwitcher]()] = &decorateSolaxPhaseSwitcherImpl{phaseSwitcher: phaseSwitcher}
}
if phaseGetter != nil {
caps[reflect.TypeFor[api.PhaseGetter]()] = &decorateSolaxPhaseGetterImpl{phaseGetter: phaseGetter}
}
if len(caps) == 0 {
return base
}
return &decorateSolaxCapable{Solax: base, caps: caps}
}
type decorateSolaxCapable struct {
*Solax
caps map[reflect.Type]any
}
func (d *decorateSolaxCapable) Capability(typ reflect.Type) (any, bool) {
c, ok := d.caps[typ]
if !ok && reflect.TypeOf(d).Implements(typ) {
return d, true
}
return c, ok
}
type decorateSolaxPhaseGetterImpl struct {
phaseGetter func() (int, error)
}
func (impl *decorateSolaxPhaseGetterImpl) GetPhases() (int, error) {
return impl.phaseGetter()
}
type decorateSolaxPhaseSwitcherImpl struct {
phaseSwitcher func(int) error
}
func (impl *decorateSolaxPhaseSwitcherImpl) Phases1p3p(p0 int) error {
return impl.phaseSwitcher(p0)
}

View file

@ -4,6 +4,7 @@ import (
"context"
"github.com/evcc-io/evcc/api"
"github.com/evcc-io/evcc/api/implement"
"github.com/evcc-io/evcc/core/loadpoint"
"github.com/evcc-io/evcc/plugin"
"github.com/evcc-io/evcc/util"
@ -14,13 +15,12 @@ func init() {
}
type SwitchSocket struct {
implement.Caps
enable func(bool) error
enabled func() (bool, error)
*switchSocket
}
//go:generate go tool decorate -f decorateSwitchSocket -b *SwitchSocket -r api.Charger -t api.MeterEnergy,api.Battery
func NewSwitchSocketFromConfig(ctx context.Context, other map[string]any) (api.Charger, error) {
var cc struct {
embed `mapstructure:",squash"`
@ -51,23 +51,26 @@ func NewSwitchSocketFromConfig(ctx context.Context, other map[string]any) (api.C
return nil, err
}
energy, err := cc.Energy.FloatGetter(ctx)
if err != nil {
return nil, err
}
soc, err := cc.Soc.FloatGetter(ctx)
if err != nil {
return nil, err
}
c := &SwitchSocket{
Caps: implement.New(),
enabled: enabled,
enable: enable,
switchSocket: NewSwitchSocket(&cc.embed, enabled, power, cc.StandbyPower),
}
return decorateSwitchSocket(c, energy, soc), nil
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
}
implement.May(c, implement.Battery(soc))
return c, nil
}
func (c *SwitchSocket) Enabled() (bool, error) {

View file

@ -1,56 +0,0 @@
package charger
// Code generated by github.com/evcc-io/evcc/cmd/tools/decorate.go. DO NOT EDIT.
import (
"reflect"
"github.com/evcc-io/evcc/api"
)
func decorateSwitchSocket(base *SwitchSocket, meterEnergy func() (float64, error), battery func() (float64, error)) api.Charger {
caps := make(map[reflect.Type]any)
if meterEnergy != nil {
caps[reflect.TypeFor[api.MeterEnergy]()] = &decorateSwitchSocketMeterEnergyImpl{meterEnergy: meterEnergy}
}
if battery != nil {
caps[reflect.TypeFor[api.Battery]()] = &decorateSwitchSocketBatteryImpl{battery: battery}
}
if len(caps) == 0 {
return base
}
return &decorateSwitchSocketCapable{SwitchSocket: base, caps: caps}
}
type decorateSwitchSocketCapable struct {
*SwitchSocket
caps map[reflect.Type]any
}
func (d *decorateSwitchSocketCapable) Capability(typ reflect.Type) (any, bool) {
c, ok := d.caps[typ]
if !ok && reflect.TypeOf(d).Implements(typ) {
return d, true
}
return c, ok
}
type decorateSwitchSocketBatteryImpl struct {
battery func() (float64, error)
}
func (impl *decorateSwitchSocketBatteryImpl) Soc() (float64, error) {
return impl.battery()
}
type decorateSwitchSocketMeterEnergyImpl struct {
meterEnergy func() (float64, error)
}
func (impl *decorateSwitchSocketMeterEnergyImpl) TotalEnergy() (float64, error) {
return impl.meterEnergy()
}

View file

@ -4,6 +4,7 @@ import (
"time"
"github.com/evcc-io/evcc/api"
"github.com/evcc-io/evcc/api/implement"
"github.com/evcc-io/evcc/meter/tasmota"
"github.com/evcc-io/evcc/util"
)
@ -15,6 +16,7 @@ import (
// Tasmota charger implementation
type Tasmota struct {
implement.Caps
conn *tasmota.Connection
*switchSocket
}
@ -23,8 +25,6 @@ func init() {
registry.Add("tasmota", NewTasmotaFromConfig)
}
//go:generate go tool decorate -f decorateTasmota -b *Tasmota -r api.Charger -t api.PhaseVoltages,api.PhaseCurrents
// NewTasmotaFromConfig creates a Tasmota charger from generic config
func NewTasmotaFromConfig(other map[string]any) (api.Charger, error) {
cc := struct {
@ -60,6 +60,7 @@ func NewTasmota(embed embed, uri, user, password, usage string, channels []int,
}
c := &Tasmota{
Caps: implement.New(),
conn: conn,
}
@ -74,7 +75,8 @@ func NewTasmota(embed embed, uri, user, password, usage string, channels []int,
}
if hasPhases || len(channels) == 3 {
return decorateTasmota(c, c.voltages, c.currents), nil
implement.Has(c, implement.PhaseVoltages(c.voltages))
implement.Has(c, implement.PhaseCurrents(c.currents))
}
return c, nil

View file

@ -1,56 +0,0 @@
package charger
// Code generated by github.com/evcc-io/evcc/cmd/tools/decorate.go. DO NOT EDIT.
import (
"reflect"
"github.com/evcc-io/evcc/api"
)
func decorateTasmota(base *Tasmota, phaseVoltages func() (float64, float64, float64, error), phaseCurrents func() (float64, float64, float64, error)) api.Charger {
caps := make(map[reflect.Type]any)
if phaseVoltages != nil {
caps[reflect.TypeFor[api.PhaseVoltages]()] = &decorateTasmotaPhaseVoltagesImpl{phaseVoltages: phaseVoltages}
}
if phaseCurrents != nil {
caps[reflect.TypeFor[api.PhaseCurrents]()] = &decorateTasmotaPhaseCurrentsImpl{phaseCurrents: phaseCurrents}
}
if len(caps) == 0 {
return base
}
return &decorateTasmotaCapable{Tasmota: base, caps: caps}
}
type decorateTasmotaCapable struct {
*Tasmota
caps map[reflect.Type]any
}
func (d *decorateTasmotaCapable) Capability(typ reflect.Type) (any, bool) {
c, ok := d.caps[typ]
if !ok && reflect.TypeOf(d).Implements(typ) {
return d, true
}
return c, ok
}
type decorateTasmotaPhaseCurrentsImpl struct {
phaseCurrents func() (float64, float64, float64, error)
}
func (impl *decorateTasmotaPhaseCurrentsImpl) Currents() (float64, float64, float64, error) {
return impl.phaseCurrents()
}
type decorateTasmotaPhaseVoltagesImpl struct {
phaseVoltages func() (float64, float64, float64, error)
}
func (impl *decorateTasmotaPhaseVoltagesImpl) Voltages() (float64, float64, float64, error) {
return impl.phaseVoltages()
}

View file

@ -26,6 +26,7 @@ import (
"github.com/WulfgarW/sensonet"
"github.com/evcc-io/evcc/api"
"github.com/evcc-io/evcc/api/implement"
"github.com/evcc-io/evcc/util"
"github.com/evcc-io/evcc/util/request"
"github.com/samber/lo"
@ -43,8 +44,6 @@ type Vaillant struct {
systemId string
}
//go:generate go tool decorate -f decorateVaillant -b *Vaillant -r api.Charger -t api.Meter,api.Battery
// NewVaillantFromConfig creates an Vaillant configurable charger from generic config
func NewVaillantFromConfig(ctx context.Context, other map[string]any) (api.Charger, error) {
cc := struct {
@ -155,14 +154,13 @@ func NewVaillantFromConfig(ctx context.Context, other map[string]any) (api.Charg
SgReady: sgr,
}
var power func() (float64, error)
if devices, _ := conn.GetMpcData(systemId); len(devices) > 0 {
power = util.Cached(func() (float64, error) {
implement.Has(res, implement.Meter(util.Cached(func() (float64, error) {
res, err := conn.GetMpcData(systemId)
return lo.SumBy(res, func(d sensonet.MpcDevice) float64 {
return d.CurrentPower
}), err
}, cc.Cache)
}, cc.Cache)))
}
heatingTemp := func(zz []sensonet.StateZone) float64 {
@ -181,9 +179,8 @@ func NewVaillantFromConfig(ctx context.Context, other map[string]any) (api.Charg
heatingTempSensor = heatingTemp(system.State.Zones) > 0
}
var temp func() (float64, error)
if !heating || heatingTempSensor {
temp = util.Cached(func() (float64, error) {
implement.Has(res, implement.Battery(util.Cached(func() (float64, error) {
system, err := conn.GetSystem(systemId)
if err != nil {
return 0, err
@ -202,10 +199,10 @@ func NewVaillantFromConfig(ctx context.Context, other map[string]any) (api.Charg
default:
return 0, api.ErrNotAvailable
}
}, cc.Cache)
}, cc.Cache)))
}
return decorateVaillant(res, power, temp), nil
return res, nil
}
func (v *Vaillant) print(chapter int, prefix string, zz ...any) {

View file

@ -1,56 +0,0 @@
package charger
// Code generated by github.com/evcc-io/evcc/cmd/tools/decorate.go. DO NOT EDIT.
import (
"reflect"
"github.com/evcc-io/evcc/api"
)
func decorateVaillant(base *Vaillant, meter func() (float64, error), battery func() (float64, error)) api.Charger {
caps := make(map[reflect.Type]any)
if meter != nil {
caps[reflect.TypeFor[api.Meter]()] = &decorateVaillantMeterImpl{meter: meter}
}
if battery != nil {
caps[reflect.TypeFor[api.Battery]()] = &decorateVaillantBatteryImpl{battery: battery}
}
if len(caps) == 0 {
return base
}
return &decorateVaillantCapable{Vaillant: base, caps: caps}
}
type decorateVaillantCapable struct {
*Vaillant
caps map[reflect.Type]any
}
func (d *decorateVaillantCapable) Capability(typ reflect.Type) (any, bool) {
c, ok := d.caps[typ]
if !ok && reflect.TypeOf(d).Implements(typ) {
return d, true
}
return c, ok
}
type decorateVaillantBatteryImpl struct {
battery func() (float64, error)
}
func (impl *decorateVaillantBatteryImpl) Soc() (float64, error) {
return impl.battery()
}
type decorateVaillantMeterImpl struct {
meter func() (float64, error)
}
func (impl *decorateVaillantMeterImpl) CurrentPower() (float64, error) {
return impl.meter()
}

View file

@ -25,6 +25,7 @@ import (
"time"
"github.com/evcc-io/evcc/api"
"github.com/evcc-io/evcc/api/implement"
"github.com/evcc-io/evcc/util"
"github.com/evcc-io/evcc/util/modbus"
"github.com/evcc-io/evcc/util/sponsor"
@ -59,6 +60,7 @@ var (
// Vestel is an api.Charger implementation for Vestel/Hymes wallboxes with Ethernet (SW modells).
// It uses Modbus TCP to communicate with the wallbox at modbus client id 255.
type Vestel struct {
implement.Caps
log *util.Logger
conn *modbus.Connection
enabled bool
@ -69,8 +71,6 @@ func init() {
registry.AddCtx("vestel", NewVestelFromConfig)
}
//go:generate go tool decorate -f decorateVestel -b *Vestel -r api.Charger -t api.PhaseSwitcher,api.PhaseGetter,api.Identifier
// NewVestelFromConfig creates a Vestel charger from generic config
func NewVestelFromConfig(ctx context.Context, other map[string]any) (api.Charger, error) {
cc := modbus.TcpSettings{
@ -99,23 +99,18 @@ func NewVestel(ctx context.Context, uri string, id uint8) (api.Charger, error) {
conn.Logger(log.TRACE)
wb := &Vestel{
Caps: implement.New(),
log: log,
conn: conn,
current: 6,
}
var (
phasesS func(int) error
phasesG func() (int, error)
)
if b, err := wb.conn.ReadInputRegisters(vestelRegNumberPhases, 1); err == nil && binary.BigEndian.Uint16(b) == 1 {
phasesS = wb.phases1p3p
phasesG = wb.getPhases
implement.Has(wb, implement.PhaseSwitcher(wb.phases1p3p))
implement.Has(wb, implement.PhaseGetter(wb.getPhases))
}
// compare firmware version to determine if RFID is available
var identify func() (string, error)
b, err := wb.conn.ReadInputRegisters(vestelRegFirmware, 50)
if err != nil {
return nil, fmt.Errorf("failed to read firmware version: %w", err)
@ -127,7 +122,7 @@ func NewVestel(ctx context.Context, uri string, id uint8) (api.Charger, error) {
if v, err := version.NewSemver(fw); err == nil {
if v.GreaterThanOrEqual(version.Must(version.NewSemver("3.156.0"))) {
// firmware >= v3.156.0 supports RFID according to https://github.com/evcc-io/evcc/issues/21359
identify = wb.identify
implement.Has(wb, implement.Identifier(wb.identify))
}
} else {
log.WARN.Printf("failed to parse firmware version %q: %v", string(b), err)
@ -150,7 +145,7 @@ func NewVestel(ctx context.Context, uri string, id uint8) (api.Charger, error) {
}
go wb.heartbeat(ctx, timeout)
return decorateVestel(wb, phasesS, phasesG, identify), nil
return wb, nil
}
func (wb *Vestel) heartbeat(ctx context.Context, timeout time.Duration) {

View file

@ -1,68 +0,0 @@
package charger
// Code generated by github.com/evcc-io/evcc/cmd/tools/decorate.go. DO NOT EDIT.
import (
"reflect"
"github.com/evcc-io/evcc/api"
)
func decorateVestel(base *Vestel, phaseSwitcher func(int) error, phaseGetter func() (int, error), identifier func() (string, error)) api.Charger {
caps := make(map[reflect.Type]any)
if phaseSwitcher != nil {
caps[reflect.TypeFor[api.PhaseSwitcher]()] = &decorateVestelPhaseSwitcherImpl{phaseSwitcher: phaseSwitcher}
}
if phaseGetter != nil {
caps[reflect.TypeFor[api.PhaseGetter]()] = &decorateVestelPhaseGetterImpl{phaseGetter: phaseGetter}
}
if identifier != nil {
caps[reflect.TypeFor[api.Identifier]()] = &decorateVestelIdentifierImpl{identifier: identifier}
}
if len(caps) == 0 {
return base
}
return &decorateVestelCapable{Vestel: base, caps: caps}
}
type decorateVestelCapable struct {
*Vestel
caps map[reflect.Type]any
}
func (d *decorateVestelCapable) Capability(typ reflect.Type) (any, bool) {
c, ok := d.caps[typ]
if !ok && reflect.TypeOf(d).Implements(typ) {
return d, true
}
return c, ok
}
type decorateVestelIdentifierImpl struct {
identifier func() (string, error)
}
func (impl *decorateVestelIdentifierImpl) Identify() (string, error) {
return impl.identifier()
}
type decorateVestelPhaseGetterImpl struct {
phaseGetter func() (int, error)
}
func (impl *decorateVestelPhaseGetterImpl) GetPhases() (int, error) {
return impl.phaseGetter()
}
type decorateVestelPhaseSwitcherImpl struct {
phaseSwitcher func(int) error
}
func (impl *decorateVestelPhaseSwitcherImpl) Phases1p3p(p0 int) error {
return impl.phaseSwitcher(p0)
}

View file

@ -24,6 +24,7 @@ import (
"fmt"
"github.com/evcc-io/evcc/api"
"github.com/evcc-io/evcc/api/implement"
"github.com/evcc-io/evcc/util"
"github.com/evcc-io/evcc/util/modbus"
"github.com/spf13/cast"
@ -31,6 +32,7 @@ import (
// Victron charger implementation
type Victron struct {
implement.Caps
conn *modbus.Connection
regs victronRegs
}
@ -76,8 +78,6 @@ func init() {
registry.AddCtx("victron-evcs", NewVictronEVCSFromConfig)
}
//go:generate go tool decorate -f decorateVictron -b *Victron -r api.Charger -t api.PhaseSwitcher,api.PhaseGetter
// NewVictronGXFromConfig creates a Victron charger from generic config
func NewVictronGXFromConfig(ctx context.Context, other map[string]any) (api.Charger, error) {
return NewVictronFromConfig(ctx, other, victronGX)
@ -112,6 +112,7 @@ func NewVictron(ctx context.Context, uri string, slaveID uint8, regs victronRegs
conn.Logger(log.TRACE)
wb := &Victron{
Caps: implement.New(),
conn: conn,
regs: regs,
}
@ -125,10 +126,6 @@ func NewVictron(ctx context.Context, uri string, slaveID uint8, regs victronRegs
return nil, errors.New("charger must be in manual mode")
}
// phase switching (EVCS only, requires hardware modification with second contactor)
var phasesS func(int) error
var phasesG func() (int, error)
if !regs.isGX {
b, err := wb.conn.ReadHoldingRegisters(wb.regs.ThreePhaseEnabled, 1)
if err != nil {
@ -136,12 +133,12 @@ func NewVictron(ctx context.Context, uri string, slaveID uint8, regs victronRegs
}
if binary.BigEndian.Uint16(b) == 1 {
phasesS = wb.phases1p3p
phasesG = wb.getPhases
implement.Has(wb, implement.PhaseSwitcher(wb.phases1p3p))
implement.Has(wb, implement.PhaseGetter(wb.getPhases))
}
}
return decorateVictron(wb, phasesS, phasesG), nil
return wb, nil
}
// Status implements the api.Charger interface

View file

@ -1,56 +0,0 @@
package charger
// Code generated by github.com/evcc-io/evcc/cmd/tools/decorate.go. DO NOT EDIT.
import (
"reflect"
"github.com/evcc-io/evcc/api"
)
func decorateVictron(base *Victron, phaseSwitcher func(int) error, phaseGetter func() (int, error)) api.Charger {
caps := make(map[reflect.Type]any)
if phaseSwitcher != nil {
caps[reflect.TypeFor[api.PhaseSwitcher]()] = &decorateVictronPhaseSwitcherImpl{phaseSwitcher: phaseSwitcher}
}
if phaseGetter != nil {
caps[reflect.TypeFor[api.PhaseGetter]()] = &decorateVictronPhaseGetterImpl{phaseGetter: phaseGetter}
}
if len(caps) == 0 {
return base
}
return &decorateVictronCapable{Victron: base, caps: caps}
}
type decorateVictronCapable struct {
*Victron
caps map[reflect.Type]any
}
func (d *decorateVictronCapable) Capability(typ reflect.Type) (any, bool) {
c, ok := d.caps[typ]
if !ok && reflect.TypeOf(d).Implements(typ) {
return d, true
}
return c, ok
}
type decorateVictronPhaseGetterImpl struct {
phaseGetter func() (int, error)
}
func (impl *decorateVictronPhaseGetterImpl) GetPhases() (int, error) {
return impl.phaseGetter()
}
type decorateVictronPhaseSwitcherImpl struct {
phaseSwitcher func(int) error
}
func (impl *decorateVictronPhaseSwitcherImpl) Phases1p3p(p0 int) error {
return impl.phaseSwitcher(p0)
}

View file

@ -16,6 +16,7 @@ import (
"github.com/cenkalti/backoff/v4"
"github.com/coder/websocket"
"github.com/evcc-io/evcc/api"
"github.com/evcc-io/evcc/api/implement"
"github.com/evcc-io/evcc/charger/warp"
"github.com/evcc-io/evcc/util"
"github.com/evcc-io/evcc/util/request"
@ -23,6 +24,7 @@ import (
type WarpWS struct {
*warp.Connection
implement.Caps
pm *warp.Connection // separate Energy Manager
// config
@ -54,8 +56,6 @@ func init() {
registry.AddCtx("warp-ws", NewWarpWSFromConfig)
}
//go:generate go tool decorate -f decorateWarpWS -b *WarpWS -r api.Charger -t api.Meter,api.MeterEnergy,api.PhaseCurrents,api.PhaseVoltages,api.Identifier,api.PhaseSwitcher,api.PhaseGetter
func NewWarpWSFromConfig(ctx context.Context, other map[string]any) (api.Charger, error) {
var cc struct {
URI string
@ -79,34 +79,29 @@ func NewWarpWSFromConfig(ctx context.Context, other map[string]any) (api.Charger
}
// Feature: Meter -> Meter is legacy API, Meters is the new API
var currentPower, totalEnergy func() (float64, error)
if w.hasFeature(warp.FeatureMeter) || w.hasFeature(warp.FeatureMeters) {
currentPower = w.currentPower
totalEnergy = w.totalEnergy
implement.Has(w, implement.Meter(w.currentPower))
implement.Has(w, implement.MeterEnergy(w.totalEnergy))
}
// Feature: Meters | MeterAllValues
var currents, voltages func() (float64, float64, float64, error)
if w.hasFeature(warp.FeatureMeters) || w.hasFeature(warp.FeatureMeterAllValues) {
currents = w.currents
voltages = w.voltages
implement.Has(w, implement.PhaseCurrents(w.currents))
implement.Has(w, implement.PhaseVoltages(w.voltages))
}
// Feature: NFC
var identify func() (string, error)
if w.hasFeature(warp.FeatureNfc) {
identify = w.identify
implement.Has(w, implement.Identifier(w.identify))
}
// Feature: Phase Switching
// only setup phase switching methods if power manager endpoint is set
var phases func(int) error
var getPhases func() (int, error)
if (w.hasFeature(warp.FeaturePhaseSwitch) || cc.EnergyManagerURI != "") && w.pm != nil {
if res, err := w.ensurePmState(); err == nil && res.ExternalControl != warp.ExternalControlDeactivated {
w.pmState = &res
phases = w.phases1p3p
getPhases = w.getPhases
implement.Has(w, implement.PhaseSwitcher(w.phases1p3p))
implement.Has(w, implement.PhaseGetter(w.getPhases))
}
}
@ -123,7 +118,7 @@ func NewWarpWSFromConfig(ctx context.Context, other map[string]any) (api.Charger
w.log.TRACE.Println("disabled phase auto switching")
}
return decorateWarpWS(w, currentPower, totalEnergy, currents, voltages, identify, phases, getPhases), nil
return w, nil
}
func NewWarpWS(ctx context.Context, uri, user, pass, emURI, emUser, emPass string, meterIndex uint) (*WarpWS, error) {
@ -131,6 +126,7 @@ func NewWarpWS(ctx context.Context, uri, user, pass, emURI, emUser, emPass strin
w := &WarpWS{
Connection: warp.NewConnection(log, uri, user, pass),
Caps: implement.New(),
log: log,
meterIndex: meterIndex,
meterMap: map[int]int{},

View file

@ -1,116 +0,0 @@
package charger
// Code generated by github.com/evcc-io/evcc/cmd/tools/decorate.go. DO NOT EDIT.
import (
"reflect"
"github.com/evcc-io/evcc/api"
)
func decorateWarpWS(base *WarpWS, meter func() (float64, error), meterEnergy func() (float64, error), phaseCurrents func() (float64, float64, float64, error), phaseVoltages func() (float64, float64, float64, error), identifier func() (string, error), phaseSwitcher func(int) error, phaseGetter func() (int, error)) api.Charger {
caps := make(map[reflect.Type]any)
if meter != nil {
caps[reflect.TypeFor[api.Meter]()] = &decorateWarpWSMeterImpl{meter: meter}
}
if meterEnergy != nil {
caps[reflect.TypeFor[api.MeterEnergy]()] = &decorateWarpWSMeterEnergyImpl{meterEnergy: meterEnergy}
}
if phaseCurrents != nil {
caps[reflect.TypeFor[api.PhaseCurrents]()] = &decorateWarpWSPhaseCurrentsImpl{phaseCurrents: phaseCurrents}
}
if phaseVoltages != nil {
caps[reflect.TypeFor[api.PhaseVoltages]()] = &decorateWarpWSPhaseVoltagesImpl{phaseVoltages: phaseVoltages}
}
if identifier != nil {
caps[reflect.TypeFor[api.Identifier]()] = &decorateWarpWSIdentifierImpl{identifier: identifier}
}
if phaseSwitcher != nil {
caps[reflect.TypeFor[api.PhaseSwitcher]()] = &decorateWarpWSPhaseSwitcherImpl{phaseSwitcher: phaseSwitcher}
}
if phaseGetter != nil {
caps[reflect.TypeFor[api.PhaseGetter]()] = &decorateWarpWSPhaseGetterImpl{phaseGetter: phaseGetter}
}
if len(caps) == 0 {
return base
}
return &decorateWarpWSCapable{WarpWS: base, caps: caps}
}
type decorateWarpWSCapable struct {
*WarpWS
caps map[reflect.Type]any
}
func (d *decorateWarpWSCapable) Capability(typ reflect.Type) (any, bool) {
c, ok := d.caps[typ]
if !ok && reflect.TypeOf(d).Implements(typ) {
return d, true
}
return c, ok
}
type decorateWarpWSIdentifierImpl struct {
identifier func() (string, error)
}
func (impl *decorateWarpWSIdentifierImpl) Identify() (string, error) {
return impl.identifier()
}
type decorateWarpWSMeterImpl struct {
meter func() (float64, error)
}
func (impl *decorateWarpWSMeterImpl) CurrentPower() (float64, error) {
return impl.meter()
}
type decorateWarpWSMeterEnergyImpl struct {
meterEnergy func() (float64, error)
}
func (impl *decorateWarpWSMeterEnergyImpl) TotalEnergy() (float64, error) {
return impl.meterEnergy()
}
type decorateWarpWSPhaseCurrentsImpl struct {
phaseCurrents func() (float64, float64, float64, error)
}
func (impl *decorateWarpWSPhaseCurrentsImpl) Currents() (float64, float64, float64, error) {
return impl.phaseCurrents()
}
type decorateWarpWSPhaseGetterImpl struct {
phaseGetter func() (int, error)
}
func (impl *decorateWarpWSPhaseGetterImpl) GetPhases() (int, error) {
return impl.phaseGetter()
}
type decorateWarpWSPhaseSwitcherImpl struct {
phaseSwitcher func(int) error
}
func (impl *decorateWarpWSPhaseSwitcherImpl) Phases1p3p(p0 int) error {
return impl.phaseSwitcher(p0)
}
type decorateWarpWSPhaseVoltagesImpl struct {
phaseVoltages func() (float64, float64, float64, error)
}
func (impl *decorateWarpWSPhaseVoltagesImpl) Voltages() (float64, float64, float64, error) {
return impl.phaseVoltages()
}

Some files were not shown because too many files have changed in this diff Show more