diff --git a/charger/easee.go b/charger/easee.go index 5c6f9cdf8..49cb282f5 100644 --- a/charger/easee.go +++ b/charger/easee.go @@ -527,13 +527,19 @@ func (c *Easee) Enable(enable bool) (err error) { // resume/stop charger action := easee.ChargePause - var targetCurrent float64 if enable { action = easee.ChargeResume if opMode == easee.ModeAwaitingAuthentication && c.authorize { action = easee.ChargeStart } - targetCurrent = 32 + } + + // Easee firmware delays ~5 min when starting at 6A. + // Vendor docs recommend 7A minimum, clamp DCC before start. + if action == easee.ChargeStart && c.current < 7 { + if err := c.MaxCurrent(7); err != nil { + return err + } } uri := fmt.Sprintf("%s/chargers/%s/commands/%s", easee.API, c.charger, action) @@ -545,19 +551,6 @@ func (c *Easee) Enable(enable bool) (err error) { return err } - if action == easee.ChargeStart { // ChargeStart does not mingle with DCC, no need for below operations - return nil - } - - if err := c.waitForDynamicChargerCurrent(targetCurrent); err != nil { - return err - } - - if enable { - // reset currents after enable, as easee automatically resets to maxA - return c.MaxCurrent(int64(c.current)) - } - return nil } diff --git a/charger/easee_test.go b/charger/easee_test.go index a9905ad42..de2d05240 100644 --- a/charger/easee_test.go +++ b/charger/easee_test.go @@ -748,3 +748,65 @@ func TestDetermineCircuit(t *testing.T) { }) } } + +func TestEasee_Enable_clampsCurrentOnChargeStart(t *testing.T) { + const chargerID = "TESTTEST" + + tests := []struct { + name string + current float64 + expectClamp bool + }{ + {"6A clamped to 7A", 6, true}, + {"7A unchanged", 7, false}, + {"10A unchanged", 10, false}, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + e := newEasee() + e.charger = chargerID + e.current = tc.current + e.dynamicChargerCurrent = tc.current + e.opMode = easee.ModeAwaitingAuthentication + e.authorize = true + e.chargerEnabled = true // skip the "enable charger once" path + + httpmock.ActivateNonDefault(e.Client) + defer httpmock.DeactivateAndReset() + + var callOrder []string + + // Mock POST charger settings (DCC clamp) + settingsURI := fmt.Sprintf("%s/chargers/%s/settings", easee.API, chargerID) + httpmock.RegisterResponder(http.MethodPost, settingsURI, func(req *http.Request) (*http.Response, error) { + callOrder = append(callOrder, "settings") + return httpmock.NewStringResponse(202, "[]"), nil + }) + + // Mock POST start_charging command + startURI := fmt.Sprintf("%s/chargers/%s/commands/%s", easee.API, chargerID, easee.ChargeStart) + httpmock.RegisterResponder(http.MethodPost, startURI, func(req *http.Request) (*http.Response, error) { + callOrder = append(callOrder, "start") + // Transition to expected state for waitForChargerEnabledState(true) + e.mux.Lock() + e.opMode = easee.ModeAwaitingStart + e.mux.Unlock() + return httpmock.NewStringResponse(200, ""), nil + }) + + err := e.Enable(true) + assert.NoError(t, err) + + if tc.expectClamp { + assert.Equal(t, []string{"settings", "start"}, callOrder, + "DCC clamp should be sent before ChargeStart") + assert.Equal(t, 7.0, e.current, "current should be clamped to 7") + } else { + assert.Equal(t, []string{"start"}, callOrder, + "no DCC clamp should be sent") + assert.Equal(t, tc.current, e.current, "current should be unchanged") + } + }) + } +}