Skip to content

Commit

Permalink
Planner: don't touch plan when disabling due to soc/energy limits rea…
Browse files Browse the repository at this point in the history
…ched (evcc-io#11030)
  • Loading branch information
andig authored Dec 19, 2023
1 parent 256a604 commit e1e703c
Show file tree
Hide file tree
Showing 23 changed files with 701 additions and 190 deletions.
8 changes: 7 additions & 1 deletion assets/css/app.css
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,11 @@
--evcc-dark-green: #0fde41;
--evcc-darker-green: #0ba631;
--evcc-darkest-green: #076f20;
--evcc-darkest-green-rgb: 11, 166, 49;
--evcc-yellow: #faf000;
--evcc-dark-yellow: #bbb400;
--evcc-orange: #ff9000;
--evcc-orange-rgb: 255, 144, 0;
--bs-gray-deep: #010322;
--bs-gray-dark: #28293e;
--bs-gray-medium: #93949e;
Expand All @@ -52,7 +55,10 @@
--evcc-transition-very-fast: 100ms;

--bs-primary: var(--evcc-darker-green);
--bs-primary-rgb: 11, 166, 49;
--bs-primary-rgb: var(--evcc-darker-green-rgb);

--bs-warning: var(--evcc-orange);
--bs-warning-rgb: var(--evcc-orange-rgb);

--bs-body-font-size: 14px;
--bs-font-sans-serif: Montserrat, system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue",
Expand Down
28 changes: 25 additions & 3 deletions assets/js/components/ChargingPlanPreview.vue
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,11 @@
<div class="justify-content-between mb-2 d-flex justify-content-between">
<div class="text-start">
<div class="label">{{ $t("main.targetChargePlan.chargeDuration") }}</div>
<div class="value text-primary d-sm-flex align-items-baseline">
<div
:class="`value d-sm-flex align-items-baseline ${
timeWarning ? 'text-warning' : 'text-primary'
}`"
>
<div>{{ planDuration }}</div>
<div v-if="fmtPower" class="extraValue text-nowrap ms-sm-1">
{{ fmtPower }}
Expand Down Expand Up @@ -47,6 +51,19 @@ export default {
return { activeIndex: null, startTime: new Date() };
},
computed: {
endTime() {
if (!this.plan?.length) {
return null;
}
const end = this.plan[this.plan.length - 1].end;
return end ? new Date(end) : null;
},
timeWarning() {
if (this.targetTime && this.endTime) {
return this.targetTime < this.endTime;
}
return false;
},
planDuration() {
return this.fmtDuration(this.duration);
},
Expand Down Expand Up @@ -112,11 +129,16 @@ export default {
end.setHours(startHour + 1);
const endHour = end.getHours();
const day = this.weekdayShort(start);
const toLate = this.targetTime && this.targetTime.getTime() <= start.getTime();
const toLate = this.targetTime && this.targetTime <= start;
// TODO: handle multiple matching time slots
const price = this.findSlotInRange(start, end, rates)?.price;
const charging = this.findSlotInRange(start, end, plan) != null;
result.push({ day, price, startHour, endHour, charging, toLate });
const warning =
charging &&
this.targetTime &&
end > this.targetTime &&
this.targetTime < this.endTime;
result.push({ day, price, startHour, endHour, charging, toLate, warning });
}
return result;
},
Expand Down
52 changes: 41 additions & 11 deletions assets/js/components/ChargingPlanSettings.vue
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,15 @@
:soc-based-planning="socBasedPlanning"
@plan-updated="(data) => updatePlan({ index: 0, ...data })"
@plan-removed="() => removePlan(0)"
@plan-preview="previewPlan"
/>
</div>
</div>
<ChargingPlanWarnings v-bind="chargingPlanWarningsProps" class="mb-4" />
<hr />
<h5>
<div class="inner">
{{ $t(`main.targetCharge.${plans.length ? "currentPlan" : "noActivePlan"}`) }}
<div class="inner" data-testid="plan-preview-title">
{{ $t(`main.targetCharge.${isPreview ? "preview" : "currentPlan"}`) }}
</div>
</h5>
<ChargingPlanPreview v-if="chargingPlanPreviewProps" v-bind="chargingPlanPreviewProps" />
Expand All @@ -33,6 +34,7 @@ import ChargingPlanWarnings from "./ChargingPlanWarnings.vue";
import formatter from "../mixins/formatter";
import collector from "../mixins/collector";
import api from "../api";
import deepEqual from "../utils/deepEqual";
const DEFAULT_TARGET_TIME = "7:00";
const LAST_TARGET_TIME_KEY = "last_target_time";
Expand Down Expand Up @@ -67,39 +69,64 @@ export default {
plan: {},
activeTab: "time",
loading: false,
isPreview: false,
};
},
computed: {
chargingPlanWarningsProps: function () {
return this.collectProps(ChargingPlanWarnings);
},
chargingPlanPreviewProps: function () {
const targetTime = this.effectivePlanTime ? new Date(this.effectivePlanTime) : null;
const { rates } = this.tariff;
const { duration, plan, power } = this.plan;
const { duration, plan, power, planTime } = this.plan;
const targetTime = planTime ? new Date(planTime) : null;
const { currency, smartCostType } = this;
return rates
? { duration, plan, power, rates, targetTime, currency, smartCostType }
: null;
},
},
watch: {
plans: {
handler() {
plans(newPlans, oldPlans) {
if (!deepEqual(newPlans, oldPlans) && newPlans.length > 0) {
this.fetchPlan();
},
deep: true,
}
},
},
mounted() {
this.fetchPlan();
if (this.plans.length > 0) {
this.fetchPlan();
}
},
methods: {
fetchPlan: async function () {
fetchActivePlan: async function () {
return await api.get(`loadpoints/${this.id}/plan`);
},
fetchPlanPreviewSoc: async function (soc, time) {
const timeISO = time.toISOString();
return await api.get(`loadpoints/${this.id}/plan/preview/soc/${soc}/${timeISO}`);
},
fetchPlanPreviewEnergy: async function (energy, time) {
const timeISO = time.toISOString();
return await api.get(`loadpoints/${this.id}/plan/preview/energy/${energy}/${timeISO}`);
},
fetchPlan: async function (preview) {
if (!this.loading) {
try {
this.loading = true;
this.plan = (await api.get(`loadpoints/${this.id}/plan`)).data.result;
let planRes = null;
if (preview && this.socBasedPlanning) {
planRes = await this.fetchPlanPreviewSoc(preview.soc, preview.time);
this.isPreview = true;
} else if (preview && !this.socBasedPlanning) {
planRes = await this.fetchPlanPreviewEnergy(preview.energy, preview.time);
this.isPreview = true;
} else {
planRes = await this.fetchActivePlan();
this.isPreview = false;
}
this.plan = planRes.data.result;
const tariffRes = await api.get(`tariff/planner`, {
validateStatus: function (status) {
Expand Down Expand Up @@ -143,6 +170,9 @@ export default {
updatePlan: function (data) {
this.$emit("plan-updated", data);
},
previewPlan: function (data) {
this.fetchPlan(data);
},
},
};
</script>
Expand Down
33 changes: 29 additions & 4 deletions assets/js/components/ChargingPlanSettingsEntry.vue
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
v-model="selectedDay"
class="form-select me-2"
data-testid="plan-day"
@change="preview"
>
<option v-for="opt in dayOptions()" :key="opt.value" :value="opt.value">
{{ opt.name }}
Expand All @@ -52,6 +53,7 @@
:step="60 * 5"
data-testid="plan-time"
required
@change="preview"
/>
</div>
<div class="col-6 d-lg-none col-form-label">
Expand All @@ -66,6 +68,7 @@
v-model="selectedSoc"
class="form-select mx-0"
data-testid="plan-soc"
@change="preview"
>
<option v-for="opt in socOptions" :key="opt.value" :value="opt.value">
{{ opt.name }}
Expand All @@ -77,6 +80,7 @@
v-model="selectedEnergy"
class="form-select mx-0"
data-testid="plan-energy"
@change="preview"
>
<option v-for="opt in energyOptions" :key="opt.energy" :value="opt.energy">
{{ opt.text }}
Expand Down Expand Up @@ -113,7 +117,7 @@
</button>
</div>
</div>
<p class="mb-0">
<p class="mb-0" data-testid="plan-entry-warnings">
<span v-if="timeInThePast" class="d-block text-danger my-2">
{{ $t("main.targetCharge.targetIsInThePast") }}
</span>
Expand All @@ -129,6 +133,8 @@ import formatter from "../mixins/formatter";
import { energyOptions } from "../utils/energyOptions";
const LAST_TARGET_TIME_KEY = "last_target_time";
const LAST_SOC_GOAL_KEY = "last_soc_goal";
const LAST_ENERGY_GOAL_KEY = "last_energy_goal";
const DEFAULT_TARGET_TIME = "7:00";
export default {
Expand All @@ -144,7 +150,7 @@ export default {
vehicleCapacity: Number,
socBasedPlanning: Boolean,
},
emits: ["plan-updated", "plan-removed"],
emits: ["plan-updated", "plan-removed", "plan-preview"],
data: function () {
return {
selectedDay: null,
Expand Down Expand Up @@ -231,23 +237,29 @@ export default {
},
initInputFields: function () {
if (!this.selectedSoc) {
this.selectedSoc = 100;
this.selectedSoc = window.localStorage[LAST_SOC_GOAL_KEY] || 100;
}
if (!this.selectedEnergy) {
this.selectedEnergy = this.vehicleCapacity || 10;
this.selectedEnergy =
window.localStorage[LAST_ENERGY_GOAL_KEY] || this.vehicleCapacity || 10;
}
let time = this.time;
if (!time) {
// no time but existing selection, keep it
if (this.selectedDay && this.selectedTime) {
this.preview();
return;
}
time = this.defaultTime();
}
const date = new Date(time);
this.selectedDay = this.fmtDayString(date);
this.selectedTime = this.fmtTimeString(date);
if (this.isNew || this.dataChanged) {
this.preview();
}
},
dayOptions: function () {
const options = [];
Expand Down Expand Up @@ -276,6 +288,12 @@ export default {
const hours = this.selectedDate.getHours();
const minutes = this.selectedDate.getMinutes();
window.localStorage[LAST_TARGET_TIME_KEY] = `${hours}:${minutes}`;
if (this.selectedSoc) {
window.localStorage[LAST_SOC_GOAL_KEY] = this.selectedSoc;
}
if (this.selectedEnergy) {
window.localStorage[LAST_ENERGY_GOAL_KEY] = this.selectedEnergy;
}
} catch (e) {
console.warn(e);
}
Expand All @@ -285,6 +303,13 @@ export default {
energy: this.selectedEnergy,
});
},
preview: function () {
this.$emit("plan-preview", {
time: this.selectedDate,
soc: this.selectedSoc,
energy: this.selectedEnergy,
});
},
toggle: function (e) {
const { checked } = e.target;
if (checked) {
Expand Down
32 changes: 28 additions & 4 deletions assets/js/components/ChargingPlanWarnings.vue
Original file line number Diff line number Diff line change
@@ -1,8 +1,5 @@
<template>
<p class="mb-0">
<span v-if="targetIsAboveVehicleLimit" class="d-block text-danger mb-1">
{{ $t("main.targetCharge.targetIsAboveVehicleLimit", { limit: vehicleLimitFmt }) }}
</span>
<p class="mb-0" data-testid="plan-warnings">
<span v-if="targetIsAboveLimit" class="d-block text-secondary mb-1">
{{ $t("main.targetCharge.targetIsAboveLimit", { limit: limitFmt }) }}
</span>
Expand All @@ -15,6 +12,12 @@
<span v-if="costLimitExists" class="d-block text-secondary mb-1">
{{ $t("main.targetCharge.costLimitIgnore", { limit: costLimitText }) }}
</span>
<span v-if="notReachableInTime" class="d-block text-warning mb-1">
{{ $t("main.targetCharge.notReachableInTime", { endTime: endTimeFmt }) }}
</span>
<span v-if="targetIsAboveVehicleLimit" class="d-block text-danger mb-1">
{{ $t("main.targetCharge.targetIsAboveVehicleLimit", { limit: vehicleLimitFmt }) }}
</span>
</p>
</template>

Expand All @@ -40,9 +43,23 @@ export default {
currency: String,
mode: String,
tariff: Object,
plan: Object,
vehicleTargetSoc: Number,
},
computed: {
endTime: function () {
if (!this.plan?.plan?.length) {
return null;
}
const { plan } = this.plan;
return plan[plan.length - 1].end;
},
endTimeFmt: function () {
if (!this.endTime) {
return "";
}
return this.fmtAbsoluteDate(new Date(this.endTime));
},
timeTooFarInTheFuture: function () {
if (!this.effectivePlanTime) {
return false;
Expand All @@ -56,6 +73,13 @@ export default {
}
return false;
},
notReachableInTime: function () {
const { planTime } = this.plan || {};
if (planTime && this.endTime) {
return new Date(planTime) < new Date(this.endTime);
}
return false;
},
targetIsAboveLimit: function () {
if (this.socBasedPlanning) {
return this.effectivePlanSoc > this.effectiveLimitSoc;
Expand Down
Loading

0 comments on commit e1e703c

Please sign in to comment.