Loadpoint: rename chargeCurrent to offeredCurrent (BC) (#20457)

Co-authored-by: Michael Geers <michael@geers.tv>
This commit is contained in:
premultiply 2025-04-09 23:04:36 +02:00 • committed by GitHub
parent 2cabe49239
commit 598cccaff1
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
10 changed files with 66 additions and 64 deletions

View file

@ -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({

View file

@ -186,7 +186,7 @@ export default {
chargerSinglePhase: Boolean,
minCurrent: Number,
maxCurrent: Number,
chargeCurrent: Number,
offeredCurrent: Number,
connectedDuration: Number,
chargeCurrents: Array,
chargeRemainingEnergy: Number,

View file

@ -15,7 +15,7 @@ function loadpoint(opts = {}) {
charging: true,
vehicleSoc: 66,
limitSoc: 90,
chargeCurrent: 7,
offeredCurrent: 7,
minCurrent: 6,
maxCurrent: 16,
activePhases: 2,

View file

@ -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,

View file

@ -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;

View file

@ -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

View file

@ -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

View file

@ -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])
}

View file

@ -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)
}
}

View file

@ -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