Don't disable for 1p3p- let charger handle session stop/restart (#8320)

This commit is contained in:
MarkusGH 2023-06-20 21:58:07 +02:00 • committed by GitHub
parent 99a63fcf4b
commit b2b4483bf9
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 34 additions and 19 deletions

View file

@ -1,6 +1,7 @@
package charger
import (
"errors"
"fmt"
"github.com/evcc-io/evcc/api"
@ -30,6 +31,7 @@ func NewConfigurableFromConfig(other map[string]interface{}) (api.Charger, error
Status, Enable, Enabled, MaxCurrent provider.Config
Identify, Phases1p3p *provider.Config
Wakeup *provider.Config
Tos bool
}
if err := util.DecodeOther(other, &cc); err != nil {
@ -66,6 +68,10 @@ func NewConfigurableFromConfig(other map[string]interface{}) (api.Charger, error
// 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")
}
phases1p3pS, err := provider.NewIntSetterFromConfig("phases", *cc.Phases1p3p)
if err != nil {
return nil, fmt.Errorf("phases: %w", err)

View file

@ -550,7 +550,12 @@ func (c *Easee) Phases1p3p(phases int) error {
uri := fmt.Sprintf("%s/chargers/%s/settings", easee.API, c.charger)
err = c.postJSONAndWait(uri, data)
if err = c.postJSONAndWait(uri, data); err != nil {
return err
}
// disable charger to activate changed settings (loadpoint will reenable it)
err = c.Enable(false)
}
}

View file

@ -52,7 +52,9 @@ const (
minActiveCurrent = 1.0 // minimum current at which a phase is treated as active
minActiveVoltage = 208 // minimum voltage at which a phase is treated as active
guardGracePeriod = 60 * time.Second // allow out of sync during this timespan
guardGracePeriod = 60 * time.Second // allow out of sync during this timespan
phaseSwitchCommandTimeout = 30 * time.Second // do not sync charger enabled/disabled state during this timespan
phaseSwitchDuration = 60 * time.Second // do not measure phases during this timespan
)
// elapsed is the time an expired timer will be set to
@ -131,6 +133,7 @@ type Loadpoint struct {
guardUpdated time.Time // Charger enabled/disabled timestamp
socUpdated time.Time // Soc updated timestamp (poll: connected)
vehicleDetect time.Time // Vehicle connected timestamp
phasesSwitched time.Time // Phase switch timestamp
vehicleDetectTicker *clock.Ticker
vehicleIdentifier string
@ -630,9 +633,9 @@ func (lp *Loadpoint) syncCharger() error {
return err
}
if enabled != lp.enabled {
if (enabled != lp.enabled) && (!lp.enabled || lp.phaseSwitchCommandTimeoutElapsed()) {
// ignore disabled state if vehicle was disconnected ^(lp.enabled && ^lp.connected)
if lp.guardGracePeriodElapsed() && (!lp.enabled || lp.connected()) {
if lp.guardGracePeriodElapsed() && lp.phaseSwitchCompleted() && (!lp.enabled || lp.connected()) {
lp.log.WARN.Printf("charger out of sync: expected %vd, got %vd", status[lp.enabled], status[enabled])
}
return lp.charger.Enable(lp.enabled)
@ -929,21 +932,16 @@ func (lp *Loadpoint) scalePhases(phases int) error {
}
if lp.GetPhases() != phases {
// disable charger - this will also stop the car charging using the api if available
if err := lp.setLimit(0, true); err != nil {
return err
}
// switch phases
if err := cp.Phases1p3p(phases); err != nil {
return fmt.Errorf("switch phases: %w", err)
}
// prevent premature measurement of active phases
lp.phasesSwitched = lp.clock.Now()
// update setting and reset timer
lp.setPhases(phases)
// allow pv mode to re-enable charger right away
lp.elapsePVTimer()
}
return nil
@ -1071,11 +1069,7 @@ func (lp *Loadpoint) pvMaxCurrent(mode api.ChargeMode, sitePower float64, batter
// switch phases up/down
if _, ok := lp.charger.(api.PhaseSwitcher); ok {
availablePower := -sitePower + lp.chargePower
// in case of scaling, keep charger disabled for this cycle
if lp.pvScalePhases(availablePower, minCurrent, maxCurrent) {
return 0
}
_ = lp.pvScalePhases(availablePower, minCurrent, maxCurrent)
}
// calculate target charge current from delta power and actual current
@ -1210,7 +1204,7 @@ func (lp *Loadpoint) updateChargeCurrents() {
lp.log.DEBUG.Printf("charge currents: %.3gA", lp.chargeCurrents)
lp.publish("chargeCurrents", lp.chargeCurrents)
if lp.charging() {
if lp.charging() && lp.phaseSwitchCompleted() {
var phases int
for _, i := range lp.chargeCurrents {
if i > minActiveCurrent {
@ -1419,6 +1413,16 @@ func (lp *Loadpoint) guardGracePeriodElapsed() bool {
return time.Since(lp.guardUpdated) > guardGracePeriod
}
// phaseSwitchCommandTimeoutElapsed returns true if phase switch command should be already processed by the charger
func (lp *Loadpoint) phaseSwitchCommandTimeoutElapsed() bool {
return time.Since(lp.phasesSwitched) > phaseSwitchCommandTimeout
}
// phaseSwitchCompleted returns true if phase switch has completed
func (lp *Loadpoint) phaseSwitchCompleted() bool {
return time.Since(lp.phasesSwitched) > phaseSwitchDuration
}
// Update is the main control function. It reevaluates meters and charger state
func (lp *Loadpoint) Update(sitePower float64, autoCharge, batteryBuffered, batteryStart bool, greenShare float64, effPrice, effCo2 *float64) {
lp.processTasks()

View file

@ -85,7 +85,7 @@ func (lp *Loadpoint) activePhases() int {
active := min(expect(vehicle), expect(physical), expect(measured))
// sanity check - we should not assume less active phases than actually measured
if measured > 0 && active < measured && lp.guardGracePeriodElapsed() {
if measured > 0 && active < measured {
lp.log.WARN.Printf("phase mismatch between %dp measured for %dp vehicle and %dp charger", measured, vehicle, physical)
}