Easee: validate charger phases (#13238)
This commit is contained in:
parent
84aea50e06
commit
bb4148b0ed
1 changed files with 84 additions and 72 deletions
156
charger/easee.go
156
charger/easee.go
|
|
@ -48,7 +48,7 @@ type Easee struct {
|
|||
lastEnergyPollTriggered time.Time
|
||||
lastOpModePollTriggered time.Time
|
||||
log *util.Logger
|
||||
mux sync.Mutex
|
||||
mux sync.RWMutex
|
||||
lastEnergyPollMux sync.Mutex
|
||||
done chan struct{}
|
||||
dynamicChargerCurrent float64
|
||||
|
|
@ -61,6 +61,7 @@ type Easee struct {
|
|||
pilotMode string
|
||||
reasonForNoCurrent int
|
||||
phaseMode int
|
||||
outputPhase int
|
||||
sessionStartEnergy *float64
|
||||
currentPower, sessionEnergy, totalEnergy,
|
||||
currentL1, currentL2, currentL3 float64
|
||||
|
|
@ -308,6 +309,8 @@ func (c *Easee) ProductUpdate(i json.RawMessage) {
|
|||
c.currentL3 = value.(float64)
|
||||
case easee.PHASE_MODE:
|
||||
c.phaseMode = value.(int)
|
||||
case easee.OUTPUT_PHASE:
|
||||
c.outputPhase = value.(int) / 10 //API gives 0,10,30 for 0,1,3p
|
||||
case easee.DYNAMIC_CHARGER_CURRENT:
|
||||
c.dynamicChargerCurrent = value.(float64)
|
||||
|
||||
|
|
@ -413,8 +416,8 @@ func (c *Easee) Status() (api.ChargeStatus, error) {
|
|||
c.updateSmartCharging()
|
||||
c.confirmStatusConsistency()
|
||||
|
||||
c.mux.Lock()
|
||||
defer c.mux.Unlock()
|
||||
c.mux.RLock()
|
||||
defer c.mux.RUnlock()
|
||||
|
||||
res := api.StatusNone
|
||||
|
||||
|
|
@ -435,6 +438,8 @@ func (c *Easee) Status() (api.ChargeStatus, error) {
|
|||
|
||||
// Enabled implements the api.Charger interface
|
||||
func (c *Easee) Enabled() (bool, error) {
|
||||
c.mux.RLock()
|
||||
defer c.mux.RUnlock()
|
||||
return c.enabled, nil
|
||||
}
|
||||
|
||||
|
|
@ -505,8 +510,8 @@ func (c *Easee) Enable(enable bool) (err error) {
|
|||
}
|
||||
|
||||
func (c *Easee) inExpectedOpMode(enable bool) bool {
|
||||
c.mux.Lock()
|
||||
defer c.mux.Unlock()
|
||||
c.mux.RLock()
|
||||
defer c.mux.RUnlock()
|
||||
|
||||
// start/resume
|
||||
if enable {
|
||||
|
|
@ -606,12 +611,12 @@ func (c *Easee) waitForChargerEnabledState(expEnabled bool) error {
|
|||
// wait for current become targetCurrent
|
||||
func (c *Easee) waitForDynamicChargerCurrent(targetCurrent float64) error {
|
||||
// check any updates received meanwhile
|
||||
c.mux.Lock()
|
||||
c.mux.RLock()
|
||||
if c.dynamicChargerCurrent == targetCurrent {
|
||||
c.mux.Unlock()
|
||||
c.mux.RUnlock()
|
||||
return nil
|
||||
}
|
||||
c.mux.Unlock()
|
||||
c.mux.RUnlock()
|
||||
|
||||
timer := time.NewTimer(c.Client.Timeout)
|
||||
for {
|
||||
|
|
@ -628,8 +633,8 @@ func (c *Easee) waitForDynamicChargerCurrent(targetCurrent float64) error {
|
|||
return nil
|
||||
}
|
||||
case <-timer.C: // time is up, bail after one final check
|
||||
c.mux.Lock()
|
||||
defer c.mux.Unlock()
|
||||
c.mux.RLock()
|
||||
defer c.mux.RUnlock()
|
||||
if c.dynamicChargerCurrent == targetCurrent {
|
||||
return nil
|
||||
}
|
||||
|
|
@ -668,11 +673,72 @@ var _ api.CurrentGetter = (*Easee)(nil)
|
|||
|
||||
// GetMaxCurrent implements the api.CurrentGetter interface
|
||||
func (c *Easee) GetMaxCurrent() (float64, error) {
|
||||
c.mux.Lock()
|
||||
defer c.mux.Unlock()
|
||||
c.mux.RLock()
|
||||
defer c.mux.RUnlock()
|
||||
return c.dynamicChargerCurrent, nil
|
||||
}
|
||||
|
||||
var _ api.Meter = (*Easee)(nil)
|
||||
|
||||
// CurrentPower implements the api.Meter interface
|
||||
func (c *Easee) CurrentPower() (float64, error) {
|
||||
if status, err := c.Status(); err != nil || status == api.StatusA {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
c.mux.RLock()
|
||||
defer c.mux.RUnlock()
|
||||
|
||||
return c.currentPower, nil
|
||||
}
|
||||
|
||||
func (c *Easee) requestLifetimeEnergyUpdate() {
|
||||
c.lastEnergyPollMux.Lock()
|
||||
defer c.lastEnergyPollMux.Unlock()
|
||||
if time.Since(c.lastEnergyPollTriggered) > time.Minute*3 { // api rate limit, max once in 3 minutes
|
||||
uri := fmt.Sprintf("%s/chargers/%s/commands/%s", easee.API, c.charger, easee.PollLifetimeEnergy)
|
||||
if _, err := c.Post(uri, request.JSONContent, request.MarshalJSON(nil)); err != nil {
|
||||
c.log.WARN.Printf("Failed to trigger an update of LIFETIME_ENERGY: %v", err)
|
||||
}
|
||||
c.lastEnergyPollTriggered = time.Now()
|
||||
}
|
||||
}
|
||||
|
||||
var _ api.ChargeRater = (*Easee)(nil)
|
||||
|
||||
// ChargedEnergy implements the api.ChargeRater interface
|
||||
func (c *Easee) ChargedEnergy() (float64, error) {
|
||||
c.mux.RLock()
|
||||
defer c.mux.RUnlock()
|
||||
|
||||
// return either the self calced session energy (current LIFETIME_ENERGY minus remembered start value),
|
||||
// or the SESSION_ENERGY value by the API. Each value could be lower than the other, depending on
|
||||
// order and receive timestamp of the product update. We want to return the higher (and newer) value.
|
||||
if c.sessionStartEnergy != nil {
|
||||
return max(c.sessionEnergy, c.totalEnergy-*c.sessionStartEnergy), nil
|
||||
}
|
||||
|
||||
return c.sessionEnergy, nil
|
||||
}
|
||||
|
||||
var _ api.PhaseCurrents = (*Easee)(nil)
|
||||
|
||||
// Currents implements the api.PhaseCurrents interface
|
||||
func (c *Easee) Currents() (float64, float64, float64, error) {
|
||||
c.mux.RLock()
|
||||
defer c.mux.RUnlock()
|
||||
return c.currentL1, c.currentL2, c.currentL3, nil
|
||||
}
|
||||
|
||||
var _ api.MeterEnergy = (*Easee)(nil)
|
||||
|
||||
// TotalEnergy implements the api.MeterEnergy interface
|
||||
func (c *Easee) TotalEnergy() (float64, error) {
|
||||
c.mux.RLock()
|
||||
defer c.mux.RUnlock()
|
||||
return c.totalEnergy, nil
|
||||
}
|
||||
|
||||
var _ api.PhaseSwitcher = (*Easee)(nil)
|
||||
|
||||
// Phases1p3p implements the api.PhaseSwitcher interface
|
||||
|
|
@ -734,67 +800,13 @@ func (c *Easee) Phases1p3p(phases int) error {
|
|||
return err
|
||||
}
|
||||
|
||||
var _ api.Meter = (*Easee)(nil)
|
||||
var _ api.PhaseGetter = (*Easee)(nil)
|
||||
|
||||
// CurrentPower implements the api.Meter interface
|
||||
func (c *Easee) CurrentPower() (float64, error) {
|
||||
if status, err := c.Status(); err != nil || status == api.StatusA {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
c.mux.Lock()
|
||||
defer c.mux.Unlock()
|
||||
|
||||
return c.currentPower, nil
|
||||
}
|
||||
|
||||
func (c *Easee) requestLifetimeEnergyUpdate() {
|
||||
c.lastEnergyPollMux.Lock()
|
||||
defer c.lastEnergyPollMux.Unlock()
|
||||
if time.Since(c.lastEnergyPollTriggered) > time.Minute*3 { // api rate limit, max once in 3 minutes
|
||||
uri := fmt.Sprintf("%s/chargers/%s/commands/%s", easee.API, c.charger, easee.PollLifetimeEnergy)
|
||||
if _, err := c.Post(uri, request.JSONContent, request.MarshalJSON(nil)); err != nil {
|
||||
c.log.WARN.Printf("Failed to trigger an update of LIFETIME_ENERGY: %v", err)
|
||||
}
|
||||
c.lastEnergyPollTriggered = time.Now()
|
||||
}
|
||||
}
|
||||
|
||||
var _ api.ChargeRater = (*Easee)(nil)
|
||||
|
||||
// ChargedEnergy implements the api.ChargeRater interface
|
||||
func (c *Easee) ChargedEnergy() (float64, error) {
|
||||
c.mux.Lock()
|
||||
defer c.mux.Unlock()
|
||||
|
||||
// return either the self calced session energy (current LIFETIME_ENERGY minus remembered start value),
|
||||
// or the SESSION_ENERGY value by the API. Each value could be lower than the other, depending on
|
||||
// order and receive timestamp of the product update. We want to return the higher (and newer) value.
|
||||
if c.sessionStartEnergy != nil {
|
||||
return max(c.sessionEnergy, c.totalEnergy-*c.sessionStartEnergy), nil
|
||||
}
|
||||
|
||||
return c.sessionEnergy, nil
|
||||
}
|
||||
|
||||
var _ api.PhaseCurrents = (*Easee)(nil)
|
||||
|
||||
// Currents implements the api.PhaseCurrents interface
|
||||
func (c *Easee) Currents() (float64, float64, float64, error) {
|
||||
c.mux.Lock()
|
||||
defer c.mux.Unlock()
|
||||
|
||||
return c.currentL1, c.currentL2, c.currentL3, nil
|
||||
}
|
||||
|
||||
var _ api.MeterEnergy = (*Easee)(nil)
|
||||
|
||||
// TotalEnergy implements the api.MeterEnergy interface
|
||||
func (c *Easee) TotalEnergy() (float64, error) {
|
||||
c.mux.Lock()
|
||||
defer c.mux.Unlock()
|
||||
|
||||
return c.totalEnergy, nil
|
||||
// GetPhases implements the api.PhaseGetter interface
|
||||
func (c *Easee) GetPhases() (int, error) {
|
||||
c.mux.RLock()
|
||||
defer c.mux.RUnlock()
|
||||
return c.outputPhase, nil
|
||||
}
|
||||
|
||||
var _ api.Identifier = (*Easee)(nil)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue