From 96e6c972561dc3dc45f4b90a134705589a032147 Mon Sep 17 00:00:00 2001 From: andig Date: Wed, 16 Oct 2024 23:06:54 +0200 Subject: [PATCH] Hardybarth Salia: add phase switching (#16683) --- charger/echarge/salia/types.go | 10 +- charger/hardybarth-salia.go | 57 ++++++- charger/hardybarth-salia_decorators.go | 222 ++++++++++++++++++++++++- 3 files changed, 273 insertions(+), 16 deletions(-) diff --git a/charger/echarge/salia/types.go b/charger/echarge/salia/types.go index f079d897be..10b315906f 100644 --- a/charger/echarge/salia/types.go +++ b/charger/echarge/salia/types.go @@ -4,6 +4,7 @@ const ( HeartBeat = "salia/heartbeat" ChargeMode = "salia/chargemode" PauseCharging = "salia/pausecharging" + SetPhase = "salia/phase_switching/setphase" GridCurrentLimit = "grid_current_limit" ) @@ -33,8 +34,13 @@ type Port struct { } } Salia struct { - ChargeMode string - PauseCharging int `json:"pausecharging,string"` + ChargeMode string + PauseCharging int `json:"pausecharging,string"` + PhaseSwitching struct { + Actual int `json:",string"` + Status string + SetPhase string `json:"setphase,omitempty"` + } `json:"phase_switching"` } Metering struct { Meter struct { diff --git a/charger/hardybarth-salia.go b/charger/hardybarth-salia.go index 2dc8adbe1e..f5a8ff4715 100644 --- a/charger/hardybarth-salia.go +++ b/charger/hardybarth-salia.go @@ -52,7 +52,7 @@ func init() { registry.Add("hardybarth-salia", NewSaliaFromConfig) } -//go:generate go run ../cmd/tools/decorate.go -f decorateSalia -b *Salia -r api.Charger -t "api.Meter,CurrentPower,func() (float64, error)" -t "api.MeterEnergy,TotalEnergy,func() (float64, error)" -t "api.PhaseCurrents,Currents,func() (float64, float64, float64, error)" +//go:generate go run ../cmd/tools/decorate.go -f decorateSalia -b *Salia -r api.Charger -t "api.Meter,CurrentPower,func() (float64, error)" -t "api.MeterEnergy,TotalEnergy,func() (float64, error)" -t "api.PhaseCurrents,Currents,func() (float64, float64, float64, error)" -t "api.PhaseSwitcher,Phases1p3p,func(int) error" -t "api.PhaseGetter,GetPhases,func() (int, error)" // NewSaliaFromConfig creates a Salia cPH2 charger from generic config func NewSaliaFromConfig(other map[string]interface{}) (api.Charger, error) { @@ -118,17 +118,34 @@ func NewSalia(uri string, cache time.Duration) (api.Charger, error) { } } - if err == nil { - go wb.heartbeat() + if err != nil { + return nil, err + } - wb.pause(false) + go wb.heartbeat() - if res.Secc.Port0.Metering.Meter.Available > 0 { - return decorateSalia(wb, wb.currentPower, wb.totalEnergy, wb.currents), nil - } + 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 + } + + if res.Secc.Port0.Salia.PhaseSwitching.Actual > 0 { + phasesG = wb.getPhases + phasesS = wb.phases1p3p } - return wb, err + return decorateSalia(wb, currentPower, totalEnergy, currents, phasesS, phasesG), nil } func (wb *Salia) heartbeat() { @@ -250,6 +267,30 @@ func (wb *Salia) currents() (float64, float64, float64, error) { // return "", api.ErrNotAvailable // } +func (wb *Salia) getPhases() (int, error) { + res, err := wb.apiG.Get() + if err != nil { + return 0, err + } + + if res.Secc.Port0.Salia.PhaseSwitching.Actual == 0 { + return 0, api.ErrNotAvailable + } + + return res.Secc.Port0.Salia.PhaseSwitching.Actual, nil +} + +func (wb *Salia) phases1p3p(phases int) error { + p, err := wb.getPhases() + if err != nil { + return err + } + if p == phases { + return nil + } + return wb.post(salia.SetPhase, "toggle") +} + var _ api.Diagnosis = (*Salia)(nil) // Diagnose implements the api.Diagnosis interface diff --git a/charger/hardybarth-salia_decorators.go b/charger/hardybarth-salia_decorators.go index 682102629d..5e2122826a 100644 --- a/charger/hardybarth-salia_decorators.go +++ b/charger/hardybarth-salia_decorators.go @@ -6,12 +6,12 @@ import ( "github.com/evcc-io/evcc/api" ) -func decorateSalia(base *Salia, meter func() (float64, error), meterEnergy func() (float64, error), phaseCurrents func() (float64, float64, float64, error)) api.Charger { +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 { switch { - case meter == nil: + case meter == nil && phaseSwitcher == nil: return base - case meter != nil && meterEnergy == nil && phaseCurrents == nil: + case meter != nil && meterEnergy == nil && phaseCurrents == nil && phaseSwitcher == nil: return &struct { *Salia api.Meter @@ -22,7 +22,7 @@ func decorateSalia(base *Salia, meter func() (float64, error), meterEnergy func( }, } - case meter != nil && meterEnergy != nil && phaseCurrents == nil: + case meter != nil && meterEnergy != nil && phaseCurrents == nil && phaseSwitcher == nil: return &struct { *Salia api.Meter @@ -37,7 +37,7 @@ func decorateSalia(base *Salia, meter func() (float64, error), meterEnergy func( }, } - case meter != nil && meterEnergy == nil && phaseCurrents != nil: + case meter != nil && meterEnergy == nil && phaseCurrents != nil && phaseSwitcher == nil: return &struct { *Salia api.Meter @@ -52,7 +52,7 @@ func decorateSalia(base *Salia, meter func() (float64, error), meterEnergy func( }, } - case meter != nil && meterEnergy != nil && phaseCurrents != nil: + case meter != nil && meterEnergy != nil && phaseCurrents != nil && phaseSwitcher == nil: return &struct { *Salia api.Meter @@ -70,6 +70,200 @@ func decorateSalia(base *Salia, meter func() (float64, error), meterEnergy func( phaseCurrents: phaseCurrents, }, } + + case meter == nil && phaseGetter == nil && phaseSwitcher != nil: + return &struct { + *Salia + api.PhaseSwitcher + }{ + Salia: base, + PhaseSwitcher: &decorateSaliaPhaseSwitcherImpl{ + phaseSwitcher: phaseSwitcher, + }, + } + + case meter != nil && meterEnergy == nil && phaseCurrents == nil && phaseGetter == nil && phaseSwitcher != nil: + return &struct { + *Salia + api.Meter + api.PhaseSwitcher + }{ + Salia: base, + Meter: &decorateSaliaMeterImpl{ + meter: meter, + }, + PhaseSwitcher: &decorateSaliaPhaseSwitcherImpl{ + phaseSwitcher: phaseSwitcher, + }, + } + + case meter != nil && meterEnergy != nil && phaseCurrents == nil && phaseGetter == nil && phaseSwitcher != nil: + return &struct { + *Salia + api.Meter + api.MeterEnergy + api.PhaseSwitcher + }{ + Salia: base, + Meter: &decorateSaliaMeterImpl{ + meter: meter, + }, + MeterEnergy: &decorateSaliaMeterEnergyImpl{ + meterEnergy: meterEnergy, + }, + PhaseSwitcher: &decorateSaliaPhaseSwitcherImpl{ + phaseSwitcher: phaseSwitcher, + }, + } + + case meter != nil && meterEnergy == nil && phaseCurrents != nil && phaseGetter == nil && phaseSwitcher != nil: + return &struct { + *Salia + api.Meter + api.PhaseCurrents + api.PhaseSwitcher + }{ + Salia: base, + Meter: &decorateSaliaMeterImpl{ + meter: meter, + }, + PhaseCurrents: &decorateSaliaPhaseCurrentsImpl{ + phaseCurrents: phaseCurrents, + }, + PhaseSwitcher: &decorateSaliaPhaseSwitcherImpl{ + phaseSwitcher: phaseSwitcher, + }, + } + + case meter != nil && meterEnergy != nil && phaseCurrents != nil && phaseGetter == nil && phaseSwitcher != nil: + return &struct { + *Salia + api.Meter + api.MeterEnergy + api.PhaseCurrents + api.PhaseSwitcher + }{ + Salia: base, + Meter: &decorateSaliaMeterImpl{ + meter: meter, + }, + MeterEnergy: &decorateSaliaMeterEnergyImpl{ + meterEnergy: meterEnergy, + }, + PhaseCurrents: &decorateSaliaPhaseCurrentsImpl{ + phaseCurrents: phaseCurrents, + }, + PhaseSwitcher: &decorateSaliaPhaseSwitcherImpl{ + phaseSwitcher: phaseSwitcher, + }, + } + + case meter == nil && phaseGetter != nil && phaseSwitcher != nil: + return &struct { + *Salia + api.PhaseGetter + api.PhaseSwitcher + }{ + Salia: base, + PhaseGetter: &decorateSaliaPhaseGetterImpl{ + phaseGetter: phaseGetter, + }, + PhaseSwitcher: &decorateSaliaPhaseSwitcherImpl{ + phaseSwitcher: phaseSwitcher, + }, + } + + case meter != nil && meterEnergy == nil && phaseCurrents == nil && phaseGetter != nil && phaseSwitcher != nil: + return &struct { + *Salia + api.Meter + api.PhaseGetter + api.PhaseSwitcher + }{ + Salia: base, + Meter: &decorateSaliaMeterImpl{ + meter: meter, + }, + PhaseGetter: &decorateSaliaPhaseGetterImpl{ + phaseGetter: phaseGetter, + }, + PhaseSwitcher: &decorateSaliaPhaseSwitcherImpl{ + phaseSwitcher: phaseSwitcher, + }, + } + + case meter != nil && meterEnergy != nil && phaseCurrents == nil && phaseGetter != nil && phaseSwitcher != nil: + return &struct { + *Salia + api.Meter + api.MeterEnergy + api.PhaseGetter + api.PhaseSwitcher + }{ + Salia: base, + Meter: &decorateSaliaMeterImpl{ + meter: meter, + }, + MeterEnergy: &decorateSaliaMeterEnergyImpl{ + meterEnergy: meterEnergy, + }, + PhaseGetter: &decorateSaliaPhaseGetterImpl{ + phaseGetter: phaseGetter, + }, + PhaseSwitcher: &decorateSaliaPhaseSwitcherImpl{ + phaseSwitcher: phaseSwitcher, + }, + } + + case meter != nil && meterEnergy == nil && phaseCurrents != nil && phaseGetter != nil && phaseSwitcher != nil: + return &struct { + *Salia + api.Meter + api.PhaseCurrents + api.PhaseGetter + api.PhaseSwitcher + }{ + Salia: base, + Meter: &decorateSaliaMeterImpl{ + meter: meter, + }, + PhaseCurrents: &decorateSaliaPhaseCurrentsImpl{ + phaseCurrents: phaseCurrents, + }, + PhaseGetter: &decorateSaliaPhaseGetterImpl{ + phaseGetter: phaseGetter, + }, + PhaseSwitcher: &decorateSaliaPhaseSwitcherImpl{ + phaseSwitcher: phaseSwitcher, + }, + } + + case meter != nil && meterEnergy != nil && phaseCurrents != nil && phaseGetter != nil && phaseSwitcher != nil: + return &struct { + *Salia + api.Meter + api.MeterEnergy + api.PhaseCurrents + api.PhaseGetter + api.PhaseSwitcher + }{ + Salia: base, + Meter: &decorateSaliaMeterImpl{ + meter: meter, + }, + MeterEnergy: &decorateSaliaMeterEnergyImpl{ + meterEnergy: meterEnergy, + }, + PhaseCurrents: &decorateSaliaPhaseCurrentsImpl{ + phaseCurrents: phaseCurrents, + }, + PhaseGetter: &decorateSaliaPhaseGetterImpl{ + phaseGetter: phaseGetter, + }, + PhaseSwitcher: &decorateSaliaPhaseSwitcherImpl{ + phaseSwitcher: phaseSwitcher, + }, + } } return nil @@ -98,3 +292,19 @@ type decorateSaliaPhaseCurrentsImpl struct { 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) +}