Planner: allow plan to take precedence over vehicle or session limits (#11013)
This commit is contained in:
parent
11c6fed4bc
commit
098a0fc2b5
7 changed files with 72 additions and 49 deletions
|
|
@ -132,6 +132,7 @@ export default {
|
|||
vehicle: Object,
|
||||
vehicleCapacity: Number,
|
||||
vehicleSoc: Number,
|
||||
vehicleTargetSoc: Number,
|
||||
},
|
||||
data: function () {
|
||||
return {
|
||||
|
|
|
|||
|
|
@ -69,6 +69,7 @@ export default {
|
|||
mode: String,
|
||||
vehicleCapacity: Number,
|
||||
vehicle: Object,
|
||||
vehicleTargetSoc: Number,
|
||||
},
|
||||
emits: ["plan-removed", "plan-updated"],
|
||||
data: function () {
|
||||
|
|
|
|||
|
|
@ -1,28 +1,22 @@
|
|||
<template>
|
||||
<p class="mb-0">
|
||||
<span v-if="timeInThePast" class="d-block text-danger mb-1">
|
||||
{{ $t("main.targetCharge.targetIsInThePast") }}
|
||||
</span>
|
||||
<span v-if="targetIsAboveVehicleLimit" class="d-block text-danger mb-1">
|
||||
{{ $t("main.targetCharge.targetIsAboveVehicleLimit", { limit: vehicleLimitFmt }) }}
|
||||
</span>
|
||||
<span v-if="['off', 'now'].includes(mode)" class="d-block text-secondary mb-1">
|
||||
{{ $t("main.targetCharge.onlyInPvMode") }}
|
||||
</span>
|
||||
<span v-if="targetIsAboveLimit" class="d-block text-danger mb-1">
|
||||
{{
|
||||
$t("main.targetCharge.targetIsAboveLimit", {
|
||||
limit: limitFmt,
|
||||
goal: goalFmt,
|
||||
})
|
||||
}}
|
||||
</span>
|
||||
<span v-if="timeInThePast" class="d-block text-danger mb-1">
|
||||
{{ $t("main.targetCharge.targetIsInThePast") }}
|
||||
<span v-if="targetIsAboveLimit" class="d-block text-secondary mb-1">
|
||||
{{ $t("main.targetCharge.targetIsAboveLimit", { limit: limitFmt }) }}
|
||||
</span>
|
||||
<span v-if="timeTooFarInTheFuture" class="d-block text-secondary mb-1">
|
||||
{{ $t("main.targetCharge.targetIsTooFarInTheFuture") }}
|
||||
</span>
|
||||
<span v-if="costLimitExists" class="d-block text-secondary mb-1">
|
||||
{{
|
||||
$t("main.targetCharge.costLimitIgnore", {
|
||||
limit: costLimitText,
|
||||
})
|
||||
}}
|
||||
{{ $t("main.targetCharge.costLimitIgnore", { limit: costLimitText }) }}
|
||||
</span>
|
||||
</p>
|
||||
</template>
|
||||
|
|
@ -50,6 +44,7 @@ export default {
|
|||
mode: String,
|
||||
tariff: Object,
|
||||
selectedTargetTime: Date,
|
||||
vehicleTargetSoc: Number,
|
||||
},
|
||||
computed: {
|
||||
timeInThePast: function () {
|
||||
|
|
@ -72,12 +67,21 @@ export default {
|
|||
}
|
||||
return this.limitEnergy && this.planEnergy > this.limitEnergy;
|
||||
},
|
||||
targetIsAboveVehicleLimit: function () {
|
||||
if (this.socBasedPlanning) {
|
||||
return this.effectivePlanSoc > (this.vehicleTargetSoc || 100);
|
||||
}
|
||||
return false;
|
||||
},
|
||||
limitFmt: function () {
|
||||
if (this.socBasedPlanning) {
|
||||
return this.fmtSoc(this.effectiveLimitSoc);
|
||||
}
|
||||
return this.fmtKWh(this.limitEnergy * 1e3);
|
||||
},
|
||||
vehicleLimitFmt: function () {
|
||||
return this.fmtSoc(this.vehicleTargetSoc);
|
||||
},
|
||||
goalFmt: function () {
|
||||
if (this.socBasedPlanning) {
|
||||
return this.fmtSoc(this.effectivePlanSoc);
|
||||
|
|
|
|||
|
|
@ -32,14 +32,17 @@
|
|||
v-show="energyLimitMarkerPosition"
|
||||
class="energy-limit-marker"
|
||||
data-bs-toggle="tooltip"
|
||||
:class="{ 'energy-limit-marker--active': energyLimitMarkerPosition < 100 }"
|
||||
:class="{
|
||||
'energy-limit-marker--active': energyLimitMarkerActive,
|
||||
'energy-limit-marker--visible': energyLimitMarkerPosition < 100,
|
||||
}"
|
||||
:style="{ left: `${energyLimitMarkerPosition}%` }"
|
||||
/>
|
||||
<div
|
||||
v-show="planMarkerAvailable"
|
||||
class="plan-marker"
|
||||
data-bs-toggle="tooltip"
|
||||
:class="{ 'plan-marker--warn': !planMarkerActive }"
|
||||
:class="{ 'plan-marker--warn': planMarkerUnreachable }"
|
||||
:style="{ left: `${planMarkerPosition}%` }"
|
||||
data-testid="plan-marker"
|
||||
@click="$emit('plan-clicked')"
|
||||
|
|
@ -141,14 +144,12 @@ export default {
|
|||
}
|
||||
return this.planMarkerPosition > 0;
|
||||
},
|
||||
planMarkerActive: function () {
|
||||
planMarkerUnreachable: function () {
|
||||
if (this.socBasedPlanning) {
|
||||
const sessionLimit = this.selectedLimitSoc || this.effectiveLimitSoc;
|
||||
const vehicleLimit = this.vehicleTargetSoc || 100;
|
||||
const maxLimit = Math.min(sessionLimit, vehicleLimit);
|
||||
return this.effectivePlanSoc <= maxLimit;
|
||||
return this.effectivePlanSoc > vehicleLimit;
|
||||
}
|
||||
return this.planEnergy <= this.limitEnergy || !this.limitEnergy;
|
||||
return false;
|
||||
},
|
||||
energyLimitMarkerPosition: function () {
|
||||
if (this.socBasedCharging) {
|
||||
|
|
@ -159,8 +160,19 @@ export default {
|
|||
}
|
||||
return 100;
|
||||
},
|
||||
energyLimitMarkerActive: function () {
|
||||
if (this.socBasedCharging) {
|
||||
return false;
|
||||
}
|
||||
if (this.planEnergy) {
|
||||
return this.limitEnergy >= this.planEnergy;
|
||||
}
|
||||
return true;
|
||||
},
|
||||
sliderActive: function () {
|
||||
return !this.vehicleTargetSoc || this.visibleLimitSoc <= this.vehicleTargetSoc;
|
||||
const isBelowVehicleLimit = this.visibleLimitSoc <= (this.vehicleTargetSoc || 100);
|
||||
const isAbovePlanLimit = this.visibleLimitSoc >= (this.effectivePlanSoc || 0);
|
||||
return isBelowVehicleLimit && isAbovePlanLimit;
|
||||
},
|
||||
progressColor: function () {
|
||||
if (this.minSocActive) {
|
||||
|
|
@ -179,14 +191,14 @@ export default {
|
|||
if (this.minSocActive) {
|
||||
return this.minSoc - this.vehicleSoc;
|
||||
}
|
||||
let soc = this.sliderActive ? this.visibleLimitSoc : this.vehicleTargetSoc;
|
||||
if (soc > this.vehicleSoc) {
|
||||
return soc - this.vehicleSoc;
|
||||
const limit = Math.min(
|
||||
this.vehicleTargetSoc || 100,
|
||||
Math.max(this.visibleLimitSoc, this.effectivePlanSoc || 0)
|
||||
);
|
||||
if (limit > this.vehicleSoc) {
|
||||
return limit - this.vehicleSoc;
|
||||
}
|
||||
} else {
|
||||
if (this.limitEnergy && this.planEnergy > this.limitEnergy) {
|
||||
return (100 / this.planEnergy) * this.limitEnergy - this.vehicleSocDisplayWidth;
|
||||
}
|
||||
return 100 - this.vehicleSocDisplayWidth;
|
||||
}
|
||||
|
||||
|
|
@ -383,12 +395,15 @@ export default {
|
|||
transform: translateX(-50%);
|
||||
width: 4px;
|
||||
opacity: 0;
|
||||
background-color: var(--evcc-dark-green);
|
||||
transition-property: opacity, left;
|
||||
background-color: var(--evcc-gray);
|
||||
transition-property: opacity, left, background-color;
|
||||
transition-timing-function: linear;
|
||||
transition-duration: var(--evcc-transition-fast);
|
||||
}
|
||||
.energy-limit-marker--active {
|
||||
background-color: var(--evcc-dark-green);
|
||||
}
|
||||
.energy-limit-marker--visible {
|
||||
opacity: 1;
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -1560,6 +1560,19 @@ func (lp *Loadpoint) Update(sitePower float64, autoCharge, batteryBuffered, batt
|
|||
case lp.scalePhasesRequired():
|
||||
err = lp.scalePhases(lp.ConfiguredPhases)
|
||||
|
||||
case lp.remoteControlled(loadpoint.RemoteHardDisable):
|
||||
remoteDisabled = loadpoint.RemoteHardDisable
|
||||
fallthrough
|
||||
|
||||
case mode == api.ModeOff:
|
||||
err = lp.setLimit(0, true)
|
||||
|
||||
// minimum or target charging
|
||||
case lp.minSocNotReached() || plannerActive:
|
||||
err = lp.fastCharging()
|
||||
lp.resetPhaseTimer()
|
||||
lp.elapsePVTimer() // let PV mode disable immediately afterwards
|
||||
|
||||
case lp.limitEnergyReached():
|
||||
lp.log.DEBUG.Printf("limitEnergy reached: %.0fkWh > %0.1fkWh", lp.getChargedEnergy()/1e3, lp.limitEnergy)
|
||||
err = lp.disableUnlessClimater()
|
||||
|
|
@ -1568,23 +1581,10 @@ func (lp *Loadpoint) Update(sitePower float64, autoCharge, batteryBuffered, batt
|
|||
lp.log.DEBUG.Printf("limitSoc reached: %.1f%% > %d%%", lp.vehicleSoc, lp.effectiveLimitSoc())
|
||||
err = lp.disableUnlessClimater()
|
||||
|
||||
case lp.remoteControlled(loadpoint.RemoteHardDisable):
|
||||
remoteDisabled = loadpoint.RemoteHardDisable
|
||||
fallthrough
|
||||
|
||||
case mode == api.ModeOff:
|
||||
err = lp.setLimit(0, true)
|
||||
|
||||
// immediate charging
|
||||
// immediate charging- must be placed after limits are evaluated
|
||||
case mode == api.ModeNow:
|
||||
err = lp.fastCharging()
|
||||
|
||||
// minimum or target charging
|
||||
case lp.minSocNotReached() || plannerActive:
|
||||
err = lp.fastCharging()
|
||||
lp.resetPhaseTimer()
|
||||
lp.elapsePVTimer() // let PV mode disable immediately afterwards
|
||||
|
||||
case mode == api.ModeMinPV || mode == api.ModePV:
|
||||
// cheap tariff
|
||||
if autoCharge && lp.EffectivePlanTime().IsZero() {
|
||||
|
|
|
|||
|
|
@ -247,7 +247,7 @@ descriptionEnergy = "Bis wann sollen {targetEnergy} ins Fahrzeug geladen sein?"
|
|||
descriptionSoc = "Wann soll das Fahrzeug auf {targetSoc}% geladen sein?"
|
||||
inactiveLabel = "Zielzeit"
|
||||
onlyInPvMode = "Ladeplanung ist nur im PV-Modus aktiv."
|
||||
planDescription = "Gib eine Abfahrtszeit an, und evcc lädt dein Fahrzeug so kostengünstig und umweltfreundlich wie möglich."
|
||||
planDescription = "Gib eine Abfahrtszeit an, und evcc lädt das Fahrzeug so kostengünstig und umweltfreundlich wie möglich."
|
||||
planDuration = "Ladedauer"
|
||||
planPeriodLabel = "Zeitraum"
|
||||
planPeriodValue = "{start} bis {end}"
|
||||
|
|
@ -256,7 +256,8 @@ priceLimit = "Preisgrenze von {price}"
|
|||
remove = "Entfernen"
|
||||
setPlan = "Ladeplan festlegen"
|
||||
setTargetTime = "keine"
|
||||
targetIsAboveLimit = "Das Ladeziel {goal} liegt über dem konfigurierten Limit {limit}. Erhöhe das Limit, um das Ziel zu erreichen."
|
||||
targetIsAboveLimit = "Das eingestellte Ladelimit von {limit} wird in diesem Zeitraum ignoriert."
|
||||
targetIsAboveVehicleLimit = "Erhöhe das Fahrzeuglimit ({limit}), um das Ladeziel zu erreichen."
|
||||
targetIsInThePast = "Wähle einen Zeitpunkt in der Zukunft, Marty."
|
||||
targetIsTooFarInTheFuture = "Wir passen den Plan an, sobald wir mehr über die Zukunft wissen."
|
||||
title = "Zielzeit"
|
||||
|
|
|
|||
|
|
@ -248,7 +248,7 @@ descriptionEnergy = "Until when should {targetEnergy} be loaded into the vehicle
|
|||
descriptionSoc = "When should the vehicle be charged to {targetSoc}%?"
|
||||
inactiveLabel = "Target time"
|
||||
onlyInPvMode = "Charging plan only works in solar mode."
|
||||
planDescription = "Enter your departure time, and evcc will charge your vehicle as cost-effectively or environmentally friendly as possible."
|
||||
planDescription = "Enter a departure time, and evcc will charge the vehicle as cost-effectively or environmentally friendly as possible."
|
||||
planDuration = "Charging time"
|
||||
planPeriodLabel = "Period"
|
||||
planPeriodValue = "{start} to {end}"
|
||||
|
|
@ -257,7 +257,8 @@ priceLimit = "price limit of {price}"
|
|||
remove = "Remove"
|
||||
setPlan = "Set a charging plan"
|
||||
setTargetTime = "none"
|
||||
targetIsAboveLimit = "Charging goal {goal} is above the configured limit {limit}. You must increase the limit to reach the goal."
|
||||
targetIsAboveLimit = "The configured charging limit of {limit} will be ignored during this period."
|
||||
targetIsAboveVehicleLimit = "Increase vehicle limit ({limit}) to reach the charging goal."
|
||||
targetIsInThePast = "Pick a time in the future, Marty."
|
||||
targetIsTooFarInTheFuture = "We will adjust the plan as soon as we know more about the future."
|
||||
title = "Target Time"
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue