Skip to content

Commit

Permalink
Easee: handle async api responses (#8433)
Browse files Browse the repository at this point in the history
  • Loading branch information
GrimmiMeloni committed Jun 14, 2023
1 parent aaa4802 commit 1ec690c
Showing 1 changed file with 94 additions and 37 deletions.
131 changes: 94 additions & 37 deletions charger/easee.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import (
"net/http"
"os"
"strconv"
"strings"
"sync"
"time"

Expand Down Expand Up @@ -57,8 +58,9 @@ type Easee struct {
phaseMode int
currentPower, sessionEnergy, totalEnergy,
currentL1, currentL2, currentL3 float64
rfid string
lp loadpoint.API
rfid string
lp loadpoint.API
respChan chan easee.SignalRCommandResponse
}

func init() {
Expand Down Expand Up @@ -96,11 +98,12 @@ func NewEasee(user, password, charger string, timeout time.Duration) (*Easee, er
}

c := &Easee{
Helper: request.NewHelper(log),
charger: charger,
log: log,
current: 6, // default current
done: make(chan struct{}),
Helper: request.NewHelper(log),
charger: charger,
log: log,
current: 6, // default current
done: make(chan struct{}),
respChan: make(chan easee.SignalRCommandResponse),
}

c.Client.Timeout = timeout
Expand Down Expand Up @@ -320,6 +323,11 @@ func (c *Easee) CommandResponse(i json.RawMessage) {
return
}
c.log.TRACE.Printf("CommandResponse %s: %+v", res.SerialNumber, res)

select {
case c.respChan <- res:
default:
}
}

func (c *Easee) chargers() ([]easee.Charger, error) {
Expand Down Expand Up @@ -379,22 +387,84 @@ func (c *Easee) Enable(enable bool) error {
}

uri := fmt.Sprintf("%s/chargers/%s/settings", easee.API, c.charger)
resp, err := c.Post(uri, request.JSONContent, request.MarshalJSON(data))
if err != nil {
if err := c.postJSONAndWait(uri, data); err != nil {
return err
}
resp.Body.Close()
}

// resume/stop charger
action := easee.ChargePause
if enable {
action = easee.ChargeResume
}

uri := fmt.Sprintf("%s/chargers/%s/commands/%s", easee.API, c.charger, action)
_, err := c.Post(uri, request.JSONContent, nil)
if err := c.postJSONAndWait(uri, nil); err != nil {
return err
}

return err
if enable {
// reset currents after enable, as easee automatically resets to maxA
return c.MaxCurrent(int64(c.current))
}

return nil
}

// posts JSON to the Easee API endpoint and waits for the async response
func (c *Easee) postJSONAndWait(uri string, data any) error {
resp, err := c.Post(uri, request.JSONContent, request.MarshalJSON(data))
if err != nil {
return err
}
defer resp.Body.Close()

if resp.StatusCode == 200 { //sync call
return nil
}

if resp.StatusCode == 202 { //async call, wait for response
var cmd easee.RestCommandResponse

if strings.Contains(uri, "/commands/") { //command endpoint
if err := json.NewDecoder(resp.Body).Decode(&cmd); err != nil {
return err
}
} else { //settings endpoint
var cmdArr []easee.RestCommandResponse
if err := json.NewDecoder(resp.Body).Decode(&cmdArr); err != nil {
return err
}

if len(cmdArr) != 0 {
cmd = cmdArr[0]
}
}

if cmd.Ticks == 0 { //Easee API thinks this was a noop
return nil
}
return c.waitForTickResponse(cmd.Ticks)
}

// all other response codes lead to an error
return fmt.Errorf("invalid status: %d", resp.StatusCode)
}

func (c *Easee) waitForTickResponse(expectedTick int64) error {
for {
select {
case cmdResp := <-c.respChan:
if cmdResp.Ticks == expectedTick {
if !cmdResp.WasAccepted {
return fmt.Errorf("command rejected: %d", cmdResp.Ticks)
}
return nil
}
case <-time.After(10 * time.Second):
return api.ErrTimeout
}
}
}

// MaxCurrent implements the api.Charger interface
Expand All @@ -405,21 +475,16 @@ func (c *Easee) MaxCurrent(current int64) error {
}

uri := fmt.Sprintf("%s/chargers/%s/settings", easee.API, c.charger)
resp, err := c.Post(uri, request.JSONContent, request.MarshalJSON(data))
if err == nil {
resp.Body.Close()
if resp.StatusCode == 202 && resp.ContentLength <= 2 {
// no tick id, Easee effectively ignored this update
return api.ErrMustRetry
}

c.mux.Lock()
defer c.mux.Unlock()
c.current = cur
c.currentUpdated = time.Now()
if err := c.postJSONAndWait(uri, data); err != nil {
return err
}

return err
c.mux.Lock()
defer c.mux.Unlock()
c.current = cur
c.currentUpdated = time.Now()

return nil
}

var _ api.PhaseSwitcher = (*Easee)(nil)
Expand Down Expand Up @@ -456,10 +521,7 @@ func (c *Easee) Phases1p3p(phases int) error {
data.DynamicCircuitCurrentP3 = &max3
}

var resp *http.Response
if resp, err = c.Post(uri, request.JSONContent, request.MarshalJSON(data)); err == nil {
resp.Body.Close()
}
err = c.postJSONAndWait(uri, data)
} else {
// charger level
if phases == 3 {
Expand All @@ -474,10 +536,7 @@ func (c *Easee) Phases1p3p(phases int) error {

uri := fmt.Sprintf("%s/chargers/%s/settings", easee.API, c.charger)

var resp *http.Response
if resp, err = c.Post(uri, request.JSONContent, request.MarshalJSON(data)); err == nil {
resp.Body.Close()
}
err = c.postJSONAndWait(uri, data)
}
}

Expand Down Expand Up @@ -553,10 +612,8 @@ func (c *Easee) updateSmartCharging() {
}

uri := fmt.Sprintf("%s/chargers/%s/settings", easee.API, c.charger)
req, err := request.New(http.MethodPost, uri, request.MarshalJSON(data), request.JSONEncoding)
if err == nil {
_, err = c.DoBody(req)
}

err := c.postJSONAndWait(uri, data)
if err != nil {
c.log.WARN.Printf("smart charging: %v", err)
return
Expand Down

0 comments on commit 1ec690c

Please sign in to comment.