Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Easee: Fix panic on op mode update #11434

Closed
wants to merge 12 commits into from
5 changes: 2 additions & 3 deletions .github/workflows/nightly.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ jobs:
${{ needs.check_date.outputs.should_run != 'false' }}
&& startsWith(github.ref, 'refs/heads/master')
&& ! contains(github.head_ref, 'refs/heads/chore/')
uses: evcc-io/evcc/.github/workflows/default.yml@master
uses: grimmimeloni/evcc/.github/workflows/default.yml@master

docker:
name: Publish Docker :nightly
Expand All @@ -43,7 +43,6 @@ jobs:
steps:
- uses: actions/checkout@v4
with:
ref: refs/heads/master # force master
fetch-depth: 0

- name: Get dist from cache
Expand All @@ -69,7 +68,7 @@ jobs:
platforms: linux/amd64,linux/arm64,linux/arm/v6
push: true
tags: |
evcc/evcc:nightly
grimmimeloni/evcc:${{ github.ref_name }}

apt:
name: Publish APT nightly
Expand Down
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ publish-release::

apt-nightly::
$(foreach file, $(wildcard $(PACKAGES)/*.deb), \
cloudsmith push deb evcc/unstable/any-distro/any-version $(file); \
cloudsmith push deb grimmimeloni/evcc/any-distro/any-version $(file); \
)

apt-release::
Expand Down
38 changes: 28 additions & 10 deletions charger/easee.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ type Easee struct {
charger string
site, circuit int
lastEnergyPollTriggered time.Time
lastOpModePollTriggered time.Time
log *util.Logger
mux sync.Mutex
lastEnergyPollMux sync.Mutex
Expand All @@ -57,6 +58,7 @@ type Easee struct {
authorize bool
enabled bool
opMode int
pilotMode string
reasonForNoCurrent int
phaseMode int
sessionStartEnergy *float64
Expand All @@ -68,6 +70,7 @@ type Easee struct {
obsC chan easee.Observation
obsTime map[easee.ObservationID]time.Time
stopTicker chan struct{}
once sync.Once
}

func init() {
Expand Down Expand Up @@ -242,22 +245,14 @@ func (c *Easee) subscribe(client signalr.Client) {
if err := <-client.Send("SubscribeWithCurrentState", c.charger, true); err != nil {
c.log.ERROR.Printf("SubscribeWithCurrentState: %v", err)
}
// poll opMode from charger as API can give outdated initial data after (re)connect
uri := fmt.Sprintf("%s/chargers/%s/commands/poll_chargeropmode", easee.API, c.charger)
if _, err := c.Post(uri, request.JSONContent, nil); err != nil {
c.log.WARN.Printf("failed to poll CHARGER_OP_MODE, results may vary: %v", err)
}
}
}
}()
}

// ProductUpdate implements the signalr receiver
func (c *Easee) ProductUpdate(i json.RawMessage) {
var (
once sync.Once
res easee.Observation
)
var res easee.Observation

if err := json.Unmarshal(i, &res); err != nil {
c.log.ERROR.Printf("invalid message: %s %v", i, err)
Expand Down Expand Up @@ -365,13 +360,15 @@ func (c *Easee) ProductUpdate(i json.RawMessage) {

// startup completed
if c.opMode != 0 {
once.Do(func() { close(c.done) })
c.once.Do(func() { close(c.done) })
}

c.opMode = opMode

case easee.REASON_FOR_NO_CURRENT:
c.reasonForNoCurrent = value.(int)
case easee.PILOT_MODE:
c.pilotMode = value.(string)
}

select {
Expand Down Expand Up @@ -411,6 +408,7 @@ func (c *Easee) chargers() ([]easee.Charger, error) {
// Status implements the api.Charger interface
func (c *Easee) Status() (api.ChargeStatus, error) {
c.updateSmartCharging()
c.confirmStatusConsistency()

c.mux.Lock()
defer c.mux.Unlock()
Expand Down Expand Up @@ -843,3 +841,23 @@ var _ loadpoint.Controller = (*Easee)(nil)
func (c *Easee) LoadpointControl(lp loadpoint.API) {
c.lp = lp
}

// checks that opMode matches powerflow and polls if inconsistent
func (c *Easee) confirmStatusConsistency() {
c.mux.Lock()
opCharging := c.opMode == easee.ModeCharging
pilotCharging := c.pilotMode == "C"
powerFlowing := c.currentPower > 0
c.mux.Unlock()

if opCharging != powerFlowing || opCharging != pilotCharging {
// poll opMode from charger as API can give outdated data after SignalR (re)connect
if time.Since(c.lastOpModePollTriggered) > time.Minute*3 { // api rate limit, max once in 3 minutes
uri := fmt.Sprintf("%s/chargers/%s/commands/poll_chargeropmode", easee.API, c.charger)
if _, err := c.Post(uri, request.JSONContent, nil); err != nil {
c.log.WARN.Printf("failed to poll CHARGER_OP_MODE, results may vary: %v", err)
}
c.lastOpModePollTriggered = time.Now()
}
}
}