From 598cccaff1767bb40a8438c6ea454dc325ee247c Mon Sep 17 00:00:00 2001 From: premultiply <4681172+premultiply@users.noreply.github.com> Date: Wed, 9 Apr 2025 23:04:36 +0200 Subject: [PATCH] Loadpoint: rename chargeCurrent to offeredCurrent (BC) (#20457) Co-authored-by: Michael Geers --- .../Loadpoints/Loadpoint.stories.js | 4 +- assets/js/components/Loadpoints/Loadpoint.vue | 2 +- .../Loadpoints/Loadpoints.stories.js | 2 +- .../components/Loadpoints/Phases.stories.js | 16 ++--- assets/js/components/Loadpoints/Phases.vue | 4 +- core/keys/loadpoint.go | 4 +- core/loadpoint.go | 68 +++++++++---------- core/loadpoint_api.go | 2 +- core/loadpoint_sync_test.go | 22 +++--- core/loadpoint_test.go | 6 +- 10 files changed, 66 insertions(+), 64 deletions(-) diff --git a/assets/js/components/Loadpoints/Loadpoint.stories.js b/assets/js/components/Loadpoints/Loadpoint.stories.js index 7f578580b..040e0e676 100644 --- a/assets/js/components/Loadpoints/Loadpoint.stories.js +++ b/assets/js/components/Loadpoints/Loadpoint.stories.js @@ -22,7 +22,7 @@ const baseState = { charging: true, vehicleSoc: 66, limitSoc: 90, - chargeCurrent: 7, + offeredCurrent: 7, minCurrent: 6, maxCurrent: 16, activePhases: 2, @@ -53,7 +53,7 @@ export const Idle = createStory({ vehicleName: "", mode: "off", charging: false, - chargeCurrent: 0, + offeredCurrent: 0, }); export const DisabledLongTitle = createStory({ diff --git a/assets/js/components/Loadpoints/Loadpoint.vue b/assets/js/components/Loadpoints/Loadpoint.vue index f172955ea..adb630ca3 100644 --- a/assets/js/components/Loadpoints/Loadpoint.vue +++ b/assets/js/components/Loadpoints/Loadpoint.vue @@ -186,7 +186,7 @@ export default { chargerSinglePhase: Boolean, minCurrent: Number, maxCurrent: Number, - chargeCurrent: Number, + offeredCurrent: Number, connectedDuration: Number, chargeCurrents: Array, chargeRemainingEnergy: Number, diff --git a/assets/js/components/Loadpoints/Loadpoints.stories.js b/assets/js/components/Loadpoints/Loadpoints.stories.js index 7853d323f..883663403 100644 --- a/assets/js/components/Loadpoints/Loadpoints.stories.js +++ b/assets/js/components/Loadpoints/Loadpoints.stories.js @@ -15,7 +15,7 @@ function loadpoint(opts = {}) { charging: true, vehicleSoc: 66, limitSoc: 90, - chargeCurrent: 7, + offeredCurrent: 7, minCurrent: 6, maxCurrent: 16, activePhases: 2, diff --git a/assets/js/components/Loadpoints/Phases.stories.js b/assets/js/components/Loadpoints/Phases.stories.js index 7e567114a..b8c86cec4 100644 --- a/assets/js/components/Loadpoints/Phases.stories.js +++ b/assets/js/components/Loadpoints/Phases.stories.js @@ -10,7 +10,7 @@ export default { phasesActive: { control: { type: "number" } }, minCurrent: { control: { type: "number" } }, maxCurrent: { control: { type: "number" } }, - chargeCurrent: { control: { type: "number" } }, + offeredCurrent: { control: { type: "number" } }, chargeCurrents: { control: { type: "object" } }, }, }; @@ -28,7 +28,7 @@ OnePhase.args = { phasesActive: 1, minCurrent: 6, maxCurrent: 16, - chargeCurrent: 8, + offeredCurrent: 8, chargeCurrents: null, }; @@ -48,14 +48,14 @@ export const RealCurrents = Template.bind({}); RealCurrents.args = { ...OnePhase.args, phasesActive: 3, - chargeCurrent: 13, + offeredCurrent: 13, chargeCurrents: [11, 9, 12], }; export const OnePhaseMoreAvailable = Template.bind({}); OnePhaseMoreAvailable.args = { ...OnePhase.args, - chargeCurrent: 12, + offeredCurrent: 12, chargeCurrents: [6, 0.2, 0], }; @@ -63,7 +63,7 @@ export const TwoPhasesActive = Template.bind({}); TwoPhasesActive.args = { ...OnePhase.args, phasesActive: 2, - chargeCurrent: 16, + offeredCurrent: 16, chargeCurrents: [16, 16, 0.3], }; @@ -71,7 +71,7 @@ export const AsymetricPhases = Template.bind({}); AsymetricPhases.args = { ...OnePhase.args, phasesActive: 2, - chargeCurrent: 16, + offeredCurrent: 16, chargeCurrents: [8, 0.9, 14], }; @@ -79,7 +79,7 @@ export const OnlySecondPhase = Template.bind({}); OnlySecondPhase.args = { ...OnePhase.args, phasesActive: 1, - chargeCurrent: 13, + offeredCurrent: 13, chargeCurrents: [0, 13, 0], }; @@ -87,7 +87,7 @@ export const MainlyThirdPhase = Template.bind({}); MainlyThirdPhase.args = { ...OnePhase.args, phasesActive: 1, - chargeCurrent: 10, + offeredCurrent: 10, chargeCurrents: [0.007, 0.009, 5.945], minCurrent: 6, maxCurrent: 20, diff --git a/assets/js/components/Loadpoints/Phases.vue b/assets/js/components/Loadpoints/Phases.vue index 6bc5740c6..03116b6b1 100644 --- a/assets/js/components/Loadpoints/Phases.vue +++ b/assets/js/components/Loadpoints/Phases.vue @@ -18,7 +18,7 @@ const MIN_ACTIVE_CURRENT = 1; export default { name: "Phases", props: { - chargeCurrent: { type: Number }, + offeredCurrent: { type: Number }, chargeCurrents: { type: Array }, phasesActive: { type: Number }, minCurrent: { type: Number }, @@ -32,7 +32,7 @@ export default { methods: { targetWidth() { const current = Math.min( - Math.max(this.minCurrent, this.chargeCurrent), + Math.max(this.minCurrent, this.offeredCurrent), this.maxCurrent ); return (100 / this.maxCurrent) * current; diff --git a/core/keys/loadpoint.go b/core/keys/loadpoint.go index f3de8c324..e3277dbb9 100644 --- a/core/keys/loadpoint.go +++ b/core/keys/loadpoint.go @@ -36,6 +36,9 @@ const ( Connected = "connected" // connected Charging = "charging" // charging + // loadpoint setpoint + OfferedCurrent = "offeredCurrent" // offered current + // smart charging SmartCostActive = "smartCostActive" // smart cost active SmartCostLimit = "smartCostLimit" // smart cost limit @@ -51,7 +54,6 @@ const ( EffectiveLimitSoc = "effectiveLimitSoc" // effective limit soc // measurements - ChargeCurrent = "chargeCurrent" // charge current ChargePower = "chargePower" // charge power ChargeCurrents = "chargeCurrents" // charge currents ChargeVoltages = "chargeVoltages" // charge voltages diff --git a/core/loadpoint.go b/core/loadpoint.go index cfd67eda7..d93c67bd9 100644 --- a/core/loadpoint.go +++ b/core/loadpoint.go @@ -118,7 +118,7 @@ type Loadpoint struct { enabled bool // Charger enabled state phases int // Charger enabled phases, guarded by mutex measuredPhases int // Charger physically measured phases - chargeCurrent float64 // Charger current limit + offeredCurrent float64 // Charger current limit socUpdated time.Time // Soc updated timestamp (poll: connected) vehicleDetect time.Time // Vehicle connected timestamp chargerSwitched time.Time // Charger enabled/disabled timestamp @@ -548,12 +548,12 @@ func (lp *Loadpoint) evVehicleSocProgressHandler(soc float64) { } } -// evChargeCurrentHandler publishes the charge current +// evChargeCurrentHandler publishes the offered current func (lp *Loadpoint) evChargeCurrentHandler(current float64) { if !lp.enabled { current = 0 } - lp.publish(keys.ChargeCurrent, current) + lp.publish(keys.OfferedCurrent, current) } // evChargeCurrentWrappedMeterHandler updates the dummy charge meter's charge power. @@ -740,12 +740,12 @@ func (lp *Loadpoint) syncCharger() error { if isCg { if current, err = cg.GetMaxCurrent(); err == nil { // smallest adjustment most PWM-Controllers can do is: 100%÷256×0,6A = 0.234A - if delta := math.Abs(lp.chargeCurrent - current); delta > 0.23 { + if delta := math.Abs(lp.offeredCurrent - current); delta > 0.23 { if shouldBeConsistent && delta >= 1 { - lp.log.WARN.Printf("charger logic error: current mismatch (got %.3gA, expected %.3gA) - make sure your interval is at least 30s", current, lp.chargeCurrent) + lp.log.WARN.Printf("charger logic error: current mismatch (got %.3gA, expected %.3gA) - make sure your interval is at least 30s", current, lp.offeredCurrent) } - lp.chargeCurrent = current - lp.bus.Publish(evChargeCurrent, lp.chargeCurrent) + lp.offeredCurrent = current + lp.bus.Publish(evChargeCurrent, lp.offeredCurrent) } } else if !errors.Is(err, api.ErrNotAvailable) { return fmt.Errorf("charger get max current: %w", err) @@ -755,12 +755,12 @@ func (lp *Loadpoint) syncCharger() error { // use measured phase currents as fallback if charger does not provide max current or does not currently relay from vehicle (TWC3) if !isCg || errors.Is(err, api.ErrNotAvailable) { // validate if current too high by more than 1A (https://github.com/evcc-io/evcc/issues/14731) - if current := lp.GetMaxPhaseCurrent(); current > lp.chargeCurrent+1.0 { + if current := lp.GetMaxPhaseCurrent(); current > lp.offeredCurrent+1.0 { if shouldBeConsistent { - lp.log.WARN.Printf("charger logic error: current mismatch (got %.3gA measured, expected %.3gA) - make sure your interval is at least 30s", current, lp.chargeCurrent) + lp.log.WARN.Printf("charger logic error: current mismatch (got %.3gA measured, expected %.3gA) - make sure your interval is at least 30s", current, lp.offeredCurrent) } - lp.chargeCurrent = current - lp.bus.Publish(evChargeCurrent, lp.chargeCurrent) + lp.offeredCurrent = current + lp.bus.Publish(evChargeCurrent, lp.offeredCurrent) } } @@ -822,27 +822,27 @@ func (lp *Loadpoint) coarseCurrent() bool { } // roundedCurrent rounds current down to full amps if charger or vehicle require it -func (lp *Loadpoint) roundedCurrent(chargeCurrent float64) float64 { +func (lp *Loadpoint) roundedCurrent(current float64) float64 { // full amps only? if lp.coarseCurrent() { - chargeCurrent = math.Trunc(chargeCurrent) + current = math.Trunc(current) } - return chargeCurrent + return current } // setLimit applies charger current limits and enables/disables accordingly -func (lp *Loadpoint) setLimit(chargeCurrent float64) error { - chargeCurrent = lp.roundedCurrent(chargeCurrent) +func (lp *Loadpoint) setLimit(current float64) error { + current = lp.roundedCurrent(current) // apply circuit limits if lp.circuit != nil { - currentLimit := lp.circuit.ValidateCurrent(lp.chargeCurrent, chargeCurrent, lp.charging()) + currentLimit := lp.circuit.ValidateCurrent(lp.offeredCurrent, current, lp.charging()) activePhases := lp.ActivePhases() - powerLimit := lp.circuit.ValidatePower(lp.chargePower, currentToPower(chargeCurrent, activePhases), lp.charging()) + powerLimit := lp.circuit.ValidatePower(lp.chargePower, currentToPower(current, activePhases), lp.charging()) currentLimitViaPower := powerToCurrent(powerLimit, activePhases) - chargeCurrent = lp.roundedCurrent(min(currentLimit, currentLimitViaPower)) + current = lp.roundedCurrent(min(currentLimit, currentLimitViaPower)) } // https://github.com/evcc-io/evcc/issues/16309 @@ -852,12 +852,12 @@ func (lp *Loadpoint) setLimit(chargeCurrent float64) error { } // set current - if chargeCurrent != lp.chargeCurrent && chargeCurrent >= effMinCurrent { + if current != lp.offeredCurrent && current >= effMinCurrent { var err error if charger, ok := lp.charger.(api.ChargerEx); ok { - err = charger.MaxCurrentMillis(chargeCurrent) + err = charger.MaxCurrentMillis(current) } else { - err = lp.charger.MaxCurrent(int64(chargeCurrent)) + err = lp.charger.MaxCurrent(int64(current)) } if err != nil { @@ -865,22 +865,22 @@ func (lp *Loadpoint) setLimit(chargeCurrent float64) error { if vv, ok := v.(api.Resurrector); ok && errors.Is(err, api.ErrAsleep) { // https://github.com/evcc-io/evcc/issues/8254 // wakeup vehicle - lp.log.DEBUG.Printf("max charge current: waking up vehicle") + lp.log.DEBUG.Printf("set charge current limit: waking up vehicle") if err := vv.WakeUp(); err != nil { return fmt.Errorf("wake-up vehicle: %w", err) } } - return fmt.Errorf("max charge current %.3gA: %w", chargeCurrent, err) + return fmt.Errorf("set charge current limit %.3gA: %w", current, err) } - lp.log.DEBUG.Printf("max charge current: %.3gA", chargeCurrent) - lp.chargeCurrent = chargeCurrent - lp.bus.Publish(evChargeCurrent, chargeCurrent) + lp.log.DEBUG.Printf("set charge current limit: %.3gA", current) + lp.offeredCurrent = current + lp.bus.Publish(evChargeCurrent, current) } // set enabled/disabled - if enabled := chargeCurrent >= effMinCurrent; enabled != lp.enabled { + if enabled := current >= effMinCurrent; enabled != lp.enabled { if err := lp.charger.Enable(enabled); err != nil { v := lp.GetVehicle() if vv, ok := v.(api.Resurrector); enabled && ok && errors.Is(err, api.ErrAsleep) { @@ -900,10 +900,10 @@ func (lp *Loadpoint) setLimit(chargeCurrent float64) error { // ensure we always re-set current when enabling charger if !enabled { - lp.chargeCurrent = 0 + lp.offeredCurrent = 0 } - lp.bus.Publish(evChargeCurrent, chargeCurrent) + lp.bus.Publish(evChargeCurrent, current) // start/stop vehicle wake-up timer if enabled { @@ -1090,7 +1090,7 @@ func (lp *Loadpoint) updateChargerStatus() (bool, error) { } // update whenever there is a state change - lp.bus.Publish(evChargeCurrent, lp.chargeCurrent) + lp.bus.Publish(evChargeCurrent, lp.offeredCurrent) } return welcomeCharge, nil @@ -1105,10 +1105,10 @@ func (lp *Loadpoint) effectiveCurrent() float64 { // adjust actual current for vehicles like Zoe where it remains below target if lp.chargeCurrents != nil { cur := max(lp.chargeCurrents[0], lp.chargeCurrents[1], lp.chargeCurrents[2]) - return min(cur+2.0, lp.chargeCurrent) + return min(cur+2.0, lp.offeredCurrent) } - return lp.chargeCurrent + return lp.offeredCurrent } // elapsePVTimer puts the pv enable/disable timer into elapsed state @@ -1767,7 +1767,7 @@ func (lp *Loadpoint) Update(sitePower, batteryBoostPower float64, rates api.Rate lp.energyMetrics.SetEnvironment(greenShare, effPrice, effCo2) // update ChargeRater here to make sure initial meter update is caught - lp.bus.Publish(evChargeCurrent, lp.chargeCurrent) + lp.bus.Publish(evChargeCurrent, lp.offeredCurrent) lp.bus.Publish(evChargePower, lp.chargePower) // update progress and soc before status is updated diff --git a/core/loadpoint_api.go b/core/loadpoint_api.go index 2e1528ebc..fc2e1a6a2 100644 --- a/core/loadpoint_api.go +++ b/core/loadpoint_api.go @@ -610,7 +610,7 @@ func (lp *Loadpoint) GetMaxPhaseCurrent() float64 { lp.RLock() defer lp.RUnlock() if lp.chargeCurrents == nil { - return lp.chargeCurrent + return lp.offeredCurrent } return max(lp.chargeCurrents[0], lp.chargeCurrents[1], lp.chargeCurrents[2]) } diff --git a/core/loadpoint_sync_test.go b/core/loadpoint_sync_test.go index 2107c91be..183cbf627 100644 --- a/core/loadpoint_sync_test.go +++ b/core/loadpoint_sync_test.go @@ -77,18 +77,18 @@ func TestSyncChargerCurrentsByGetter(t *testing.T) { cg.EXPECT().GetMaxCurrent().Return(tc.actualCurrent, nil).MaxTimes(1) lp := &Loadpoint{ - log: util.NewLogger("foo"), - bus: evbus.New(), - clock: clock.New(), - charger: charger, - status: api.StatusC, - enabled: true, - phases: 3, - chargeCurrent: tc.lpCurrent, + log: util.NewLogger("foo"), + bus: evbus.New(), + clock: clock.New(), + charger: charger, + status: api.StatusC, + enabled: true, + phases: 3, + offeredCurrent: tc.lpCurrent, } require.NoError(t, lp.syncCharger()) - assert.Equal(t, tc.outCurrent, lp.chargeCurrent) + assert.Equal(t, tc.outCurrent, lp.offeredCurrent) } } @@ -120,12 +120,12 @@ func TestSyncChargerCurrentsByMeasurement(t *testing.T) { status: api.StatusC, enabled: true, phases: 3, - chargeCurrent: tc.lpCurrent, + offeredCurrent: tc.lpCurrent, chargeCurrents: []float64{tc.actualCurrent, 0, 0}, } require.NoError(t, lp.syncCharger()) - assert.Equal(t, tc.outCurrent, lp.chargeCurrent) + assert.Equal(t, tc.outCurrent, lp.offeredCurrent) } } diff --git a/core/loadpoint_test.go b/core/loadpoint_test.go index 76b26ca5a..c435bf203 100644 --- a/core/loadpoint_test.go +++ b/core/loadpoint_test.go @@ -420,7 +420,7 @@ func TestDisableAndEnableAtTargetSoc(t *testing.T) { attachListeners(t, lp) lp.enabled = true - lp.chargeCurrent = minA + lp.offeredCurrent = minA lp.status = api.StatusC t.Log("charging below soc target") @@ -490,7 +490,7 @@ func TestSetModeAndSocAtDisconnect(t *testing.T) { attachListeners(t, lp) lp.enabled = true - lp.chargeCurrent = minA + lp.offeredCurrent = minA lp.mode = api.ModeNow t.Log("charging at min") @@ -556,7 +556,7 @@ func TestChargedEnergyAtDisconnect(t *testing.T) { attachListeners(t, lp) lp.enabled = true - lp.chargeCurrent = maxA + lp.offeredCurrent = maxA lp.mode = api.ModeNow // attach cache for verifying values