Grid Charge UI: toggle limit (#25131)
This commit is contained in:
parent
a4badb9e61
commit
ab46d8150a
16 changed files with 184 additions and 64 deletions
|
|
@ -245,6 +245,7 @@
|
|||
v-if="isModalVisible"
|
||||
v-show="gridTabActive"
|
||||
:current-limit="batteryGridChargeLimit"
|
||||
:last-limit="lastSmartCostLimit"
|
||||
:smart-cost-type="smartCostType"
|
||||
:currency="currency"
|
||||
:tariff="gridChargeTariff"
|
||||
|
|
@ -263,6 +264,7 @@ import GenericModal from "../Helper/GenericModal.vue";
|
|||
import formatter, { POWER_UNIT } from "@/mixins/formatter";
|
||||
import collector from "@/mixins/collector.js";
|
||||
import api from "@/api";
|
||||
import settings from "@/settings";
|
||||
import { defineComponent, type PropType } from "vue";
|
||||
import type { BatteryMeter, SelectOption, CURRENCY, Forecast } from "@/types/evcc";
|
||||
import { SMART_COST_TYPE } from "@/types/evcc";
|
||||
|
|
@ -389,6 +391,9 @@ export default defineComponent({
|
|||
return `${name}${formattedEnergy}${formattedSoc}`;
|
||||
});
|
||||
},
|
||||
lastSmartCostLimit() {
|
||||
return settings.lastBatterySmartCostLimit;
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
prioritySoc(soc) {
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@
|
|||
</div>
|
||||
</div>
|
||||
<LoadpointSettingsModal
|
||||
:id="id"
|
||||
v-bind="settingsModal"
|
||||
@maxcurrent-updated="setMaxCurrent"
|
||||
@mincurrent-updated="setMinCurrent"
|
||||
|
|
@ -133,7 +134,7 @@ export default defineComponent({
|
|||
},
|
||||
mixins: [formatter, collector],
|
||||
props: {
|
||||
id: String,
|
||||
id: { type: String, required: true },
|
||||
single: Boolean,
|
||||
|
||||
// main
|
||||
|
|
@ -224,6 +225,8 @@ export default defineComponent({
|
|||
gridConfigured: Boolean,
|
||||
pvConfigured: Boolean,
|
||||
forecast: Object as PropType<Forecast>,
|
||||
lastSmartCostLimit: Number,
|
||||
lastSmartFeedInPriorityLimit: Number,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
|
|
|
|||
|
|
@ -10,10 +10,11 @@
|
|||
<div class="container">
|
||||
<SmartCostLimit
|
||||
:current-limit="smartCostLimit"
|
||||
:last-limit="lastSmartCostLimit"
|
||||
:smart-cost-type="smartCostType"
|
||||
:currency="currency"
|
||||
is-loadpoint
|
||||
:loadpoint-id="Number(loadpointId)"
|
||||
:loadpoint-id="loadpointId"
|
||||
:multiple-loadpoints="multipleLoadpoints"
|
||||
:possible="smartCostAvailable"
|
||||
:tariff="forecast?.planner"
|
||||
|
|
@ -21,8 +22,9 @@
|
|||
/>
|
||||
<SmartFeedInPriority
|
||||
:current-limit="smartFeedInPriorityLimit"
|
||||
:last-limit="lastSmartFeedInPriorityLimit"
|
||||
:currency="currency"
|
||||
:loadpoint-id="Number(loadpointId)"
|
||||
:loadpoint-id="loadpointId"
|
||||
:multiple-loadpoints="multipleLoadpoints"
|
||||
:possible="smartFeedInPriorityAvailable"
|
||||
:tariff="forecast?.feedin"
|
||||
|
|
@ -161,7 +163,7 @@ export default defineComponent({
|
|||
},
|
||||
mixins: [formatter, collector],
|
||||
props: {
|
||||
id: [String, Number],
|
||||
id: { type: String, required: true },
|
||||
phasesConfigured: { type: Number, default: 0 },
|
||||
chargerPhases1p3p: Boolean,
|
||||
chargerSinglePhase: Boolean,
|
||||
|
|
@ -181,6 +183,8 @@ export default defineComponent({
|
|||
currency: String as PropType<CURRENCY>,
|
||||
multipleLoadpoints: Boolean,
|
||||
forecast: Object as PropType<Forecast>,
|
||||
lastSmartCostLimit: Number,
|
||||
lastSmartFeedInPriorityLimit: Number,
|
||||
},
|
||||
emits: [
|
||||
"phasesconfigured-updated",
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
<SmartTariffBase
|
||||
v-bind="labels"
|
||||
:current-limit="currentLimit"
|
||||
:last-limit="lastLimit"
|
||||
:is-co2="isCo2"
|
||||
:currency="currency"
|
||||
:apply-all="multipleLoadpoints && isLoadpoint"
|
||||
|
|
@ -21,6 +22,8 @@
|
|||
import SmartTariffBase from "./SmartTariffBase.vue";
|
||||
import { defineComponent, type PropType } from "vue";
|
||||
import api from "@/api";
|
||||
import { setLoadpointLastSmartCostLimit } from "@/uiLoadpoints";
|
||||
import settings from "@/settings";
|
||||
import { type CURRENCY, SMART_COST_TYPE } from "@/types/evcc";
|
||||
import { type ForecastSlot } from "../Forecast/types";
|
||||
|
||||
|
|
@ -36,8 +39,9 @@ export default defineComponent({
|
|||
currency: String as PropType<CURRENCY>,
|
||||
multipleLoadpoints: Boolean,
|
||||
isLoadpoint: Boolean,
|
||||
loadpointId: Number,
|
||||
loadpointId: String,
|
||||
possible: Boolean,
|
||||
lastLimit: Number,
|
||||
tariff: Array as PropType<ForecastSlot[]>,
|
||||
},
|
||||
computed: {
|
||||
|
|
@ -69,18 +73,27 @@ export default defineComponent({
|
|||
// Smart cost: charge when costs are below or equal to limit
|
||||
return value <= this.currentLimit;
|
||||
},
|
||||
async saveLimit(limit: string) {
|
||||
async saveLimit(limit: number) {
|
||||
// save last selected value to be suggest again when reactivating limit
|
||||
this.saveLastLimit(limit);
|
||||
|
||||
const url = this.isLoadpoint
|
||||
? `loadpoints/${this.loadpointId}/smartcostlimit`
|
||||
: "batterygridchargelimit";
|
||||
|
||||
if (limit === "null") {
|
||||
await api.delete(url);
|
||||
await api.post(`${url}/${encodeURIComponent(limit)}`);
|
||||
},
|
||||
saveLastLimit(limit: number) {
|
||||
if (this.isLoadpoint) {
|
||||
setLoadpointLastSmartCostLimit(this.loadpointId!, limit);
|
||||
} else {
|
||||
await api.post(`${url}/${encodeURIComponent(limit)}`);
|
||||
settings.lastBatterySmartCostLimit = limit;
|
||||
}
|
||||
},
|
||||
async deleteLimit() {
|
||||
// save last selected value to be suggest again when reactivating limit
|
||||
this.saveLastLimit(this.currentLimit || 0);
|
||||
|
||||
const url = this.isLoadpoint
|
||||
? `loadpoints/${this.loadpointId}/smartcostlimit`
|
||||
: "batterygridchargelimit";
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
<SmartTariffBase
|
||||
v-bind="labels"
|
||||
:current-limit="currentLimit"
|
||||
:last-limit="lastLimit"
|
||||
:currency="currency"
|
||||
:apply-all="multipleLoadpoints"
|
||||
:possible="possible"
|
||||
|
|
@ -23,6 +24,7 @@ import { defineComponent, type PropType } from "vue";
|
|||
import SmartTariffBase from "./SmartTariffBase.vue";
|
||||
import api from "@/api";
|
||||
import { type CURRENCY } from "@/types/evcc";
|
||||
import { setLoadpointLastSmartFeedInPriorityLimit } from "@/uiLoadpoints";
|
||||
|
||||
export default defineComponent({
|
||||
name: "SmartFeedInPriority",
|
||||
|
|
@ -32,8 +34,9 @@ export default defineComponent({
|
|||
type: [Number, null] as PropType<number | null>,
|
||||
required: true,
|
||||
},
|
||||
lastLimit: Number,
|
||||
currency: String as PropType<CURRENCY>,
|
||||
loadpointId: { type: Number, required: true },
|
||||
loadpointId: { type: String, required: true },
|
||||
multipleLoadpoints: Boolean,
|
||||
possible: Boolean,
|
||||
tariff: Array,
|
||||
|
|
@ -62,16 +65,20 @@ export default defineComponent({
|
|||
// Smart feed-in priority: pause when rates are above or equal to limit
|
||||
return value >= this.currentLimit;
|
||||
},
|
||||
async saveLimit(limit: string) {
|
||||
const url = `loadpoints/${this.loadpointId}/smartfeedinprioritylimit`;
|
||||
async saveLimit(limit: number) {
|
||||
// save last selected value to be suggest again when reactivating limit
|
||||
this.saveLastLimit(limit);
|
||||
|
||||
if (limit === "null") {
|
||||
await api.delete(url);
|
||||
} else {
|
||||
await api.post(`${url}/${encodeURIComponent(limit)}`);
|
||||
}
|
||||
const url = `loadpoints/${this.loadpointId}/smartfeedinprioritylimit`;
|
||||
await api.post(`${url}/${encodeURIComponent(limit)}`);
|
||||
},
|
||||
saveLastLimit(limit: number) {
|
||||
setLoadpointLastSmartFeedInPriorityLimit(this.loadpointId, limit);
|
||||
},
|
||||
async deleteLimit() {
|
||||
// save last selected value to be suggest again when reactivating limit
|
||||
this.saveLastLimit(this.currentLimit || 0);
|
||||
|
||||
const url = `loadpoints/${this.loadpointId}/smartfeedinprioritylimit`;
|
||||
await api.delete(url);
|
||||
},
|
||||
|
|
|
|||
|
|
@ -4,22 +4,38 @@
|
|||
<p>
|
||||
{{ description }}
|
||||
</p>
|
||||
<div class="row mb-3">
|
||||
<div class="row mb-3 align-items-center">
|
||||
<label :for="formId" class="col-sm-4 col-form-label pt-0 pt-sm-2">
|
||||
{{ limitLabel }}
|
||||
</label>
|
||||
<div class="col-sm-8 col-lg-4 pe-0">
|
||||
<select
|
||||
:id="formId"
|
||||
v-model.number="selectedLimit"
|
||||
class="form-select form-select-sm mb-1"
|
||||
@change="changeLimit"
|
||||
>
|
||||
<option value="null">{{ $t("smartCost.none") }}</option>
|
||||
<option v-for="{ value, name } in limitOptions" :key="value" :value="value">
|
||||
{{ name }}
|
||||
</option>
|
||||
</select>
|
||||
<div class="input-group input-group-sm mb-1 mb-lg-0">
|
||||
<div class="input-group-text">
|
||||
<div class="form-check form-switch m-0">
|
||||
<input
|
||||
:id="formId + 'Active'"
|
||||
:checked="active"
|
||||
class="form-check-input"
|
||||
type="checkbox"
|
||||
role="switch"
|
||||
:aria-label="$t('smartCost.enable')"
|
||||
@change="toggleActive"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<select
|
||||
:id="formId"
|
||||
v-model.number="selectedLimit"
|
||||
class="form-select form-select-sm"
|
||||
:disabled="!active"
|
||||
:aria-label="limitLabel"
|
||||
@change="changeLimit"
|
||||
>
|
||||
<option v-for="{ value, name } in limitOptions" :key="value" :value="value">
|
||||
{{ name }}
|
||||
</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
v-if="applyToAllVisible"
|
||||
|
|
@ -32,7 +48,7 @@
|
|||
</div>
|
||||
</div>
|
||||
<div class="justify-content-between mb-2 d-flex justify-content-between">
|
||||
<div class="text-start">
|
||||
<div class="text-start" data-testid="active-hours">
|
||||
<div class="label">
|
||||
{{ activeHoursLabel }}
|
||||
</div>
|
||||
|
|
@ -91,6 +107,7 @@ export default defineComponent({
|
|||
type: [Number, null] as PropType<number | null>,
|
||||
required: true,
|
||||
},
|
||||
lastLimit: { type: Number, default: 0 },
|
||||
isCo2: Boolean,
|
||||
currency: String as PropType<CURRENCY>,
|
||||
applyAll: Boolean,
|
||||
|
|
@ -118,6 +135,7 @@ export default defineComponent({
|
|||
selectedLimit: null as number | null,
|
||||
activeIndex: null as number | null,
|
||||
applyToAllVisible: false,
|
||||
active: false,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
|
|
@ -136,7 +154,7 @@ export default defineComponent({
|
|||
|
||||
const values = [] as number[];
|
||||
const stepSize = this.optionStepSize;
|
||||
for (let i = 1; i <= 100; i++) {
|
||||
for (let i = 0; i <= 100; i++) {
|
||||
const value = this.optionStartValue + stepSize * i;
|
||||
if (max !== undefined && value > max + stepSize) break;
|
||||
values.push(this.roundLimit(value) as number);
|
||||
|
|
@ -204,14 +222,14 @@ export default defineComponent({
|
|||
const value = this.findRateInRange(start, end, rates)?.value;
|
||||
const active =
|
||||
this.limitDirection === "below" &&
|
||||
this.selectedLimit !== null &&
|
||||
this.currentLimit !== null &&
|
||||
value !== undefined &&
|
||||
value <= this.selectedLimit;
|
||||
value <= this.currentLimit;
|
||||
const warning =
|
||||
this.limitDirection === "above" &&
|
||||
this.selectedLimit !== null &&
|
||||
this.currentLimit !== null &&
|
||||
value !== undefined &&
|
||||
value >= this.selectedLimit;
|
||||
value >= this.currentLimit;
|
||||
|
||||
return {
|
||||
day: this.weekdayShort(start),
|
||||
|
|
@ -275,17 +293,26 @@ export default defineComponent({
|
|||
},
|
||||
},
|
||||
watch: {
|
||||
currentLimit(limit) {
|
||||
this.selectedLimit = this.roundLimit(limit);
|
||||
currentLimit() {
|
||||
this.initLimit();
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this.selectedLimit = this.roundLimit(this.currentLimit);
|
||||
this.initLimit();
|
||||
},
|
||||
methods: {
|
||||
roundLimit(limit: number | null): number | null {
|
||||
return limit === null ? null : Math.round(limit * 1000) / 1000;
|
||||
},
|
||||
initLimit() {
|
||||
if (this.currentLimit === null) {
|
||||
this.active = false;
|
||||
this.selectedLimit = this.lastLimit;
|
||||
} else {
|
||||
this.active = true;
|
||||
this.selectedLimit = this.roundLimit(this.currentLimit);
|
||||
}
|
||||
},
|
||||
formatLimit(limit: number | null): string {
|
||||
if (limit === null) {
|
||||
return this.$t("smartCost.none");
|
||||
|
|
@ -338,18 +365,27 @@ export default defineComponent({
|
|||
// 3 decimal precision
|
||||
const valueRounded = Math.ceil(value * 1000) / 1000;
|
||||
this.selectedLimit = valueRounded;
|
||||
this.saveLimit(`${valueRounded}`);
|
||||
this.active = true;
|
||||
this.saveLimit(valueRounded);
|
||||
}
|
||||
},
|
||||
changeLimit($event: Event) {
|
||||
const value = ($event.target as HTMLSelectElement).value;
|
||||
if (value === "null") {
|
||||
this.resetLimit();
|
||||
const value = parseFloat(($event.target as HTMLSelectElement).value);
|
||||
this.saveLimit(value);
|
||||
},
|
||||
toggleActive($event: Event) {
|
||||
const active = ($event.target as HTMLInputElement).checked;
|
||||
if (active) {
|
||||
this.saveLimit(this.lastLimit);
|
||||
} else {
|
||||
this.saveLimit(value);
|
||||
this.resetLimit();
|
||||
}
|
||||
this.active = active;
|
||||
if (this.applyAll) {
|
||||
this.applyToAllVisible = true;
|
||||
}
|
||||
},
|
||||
saveLimit(limit: string) {
|
||||
saveLimit(limit: number) {
|
||||
this.$emit("save-limit", limit);
|
||||
if (this.applyAll) {
|
||||
this.applyToAllVisible = true;
|
||||
|
|
@ -362,7 +398,7 @@ export default defineComponent({
|
|||
}
|
||||
},
|
||||
applyToAll() {
|
||||
this.$emit("apply-to-all", this.selectedLimit);
|
||||
this.$emit("apply-to-all", this.currentLimit);
|
||||
this.applyToAllVisible = false;
|
||||
},
|
||||
},
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ const SAVINGS_REGION = "savings_region";
|
|||
const SESSIONS_GROUP = "sessions_group";
|
||||
const SESSIONS_TYPE = "sessions_type";
|
||||
const SETTINGS_SOLAR_ADJUSTED = "settings_solar_adjusted";
|
||||
const LAST_BATTERY_SMART_COST_LIMIT = "last_battery_smart_cost_limit";
|
||||
|
||||
function read(key: string) {
|
||||
return window.localStorage[key];
|
||||
|
|
@ -48,6 +49,16 @@ function saveBool(key: string) {
|
|||
};
|
||||
}
|
||||
|
||||
function readNumber(key: string) {
|
||||
return read(key) ? parseFloat(read(key)) : undefined;
|
||||
}
|
||||
|
||||
function saveNumber(key: string) {
|
||||
return (value: number | undefined) => {
|
||||
save(key)(value ? value.toString() : null);
|
||||
};
|
||||
}
|
||||
|
||||
function readArray(key: string) {
|
||||
const value = read(key);
|
||||
return value ? value.split(",") : [];
|
||||
|
|
@ -83,6 +94,8 @@ export interface LoadpointSettings {
|
|||
order?: number;
|
||||
visible?: boolean;
|
||||
info?: SessionInfoKey;
|
||||
lastSmartCostLimit?: number;
|
||||
lastSmartFeedInPriorityLimit?: number;
|
||||
}
|
||||
|
||||
export interface Settings {
|
||||
|
|
@ -103,6 +116,7 @@ export interface Settings {
|
|||
sessionsType: string;
|
||||
solarAdjusted: boolean;
|
||||
loadpoints: Record<string, LoadpointSettings>;
|
||||
lastBatterySmartCostLimit: number | undefined;
|
||||
}
|
||||
|
||||
const settings: Settings = reactive({
|
||||
|
|
@ -123,6 +137,7 @@ const settings: Settings = reactive({
|
|||
sessionsType: read(SESSIONS_TYPE),
|
||||
solarAdjusted: readBool(SETTINGS_SOLAR_ADJUSTED),
|
||||
loadpoints: readJSON(LOADPOINTS),
|
||||
lastBatterySmartCostLimit: readNumber(LAST_BATTERY_SMART_COST_LIMIT),
|
||||
});
|
||||
|
||||
watch(() => settings.locale, save(SETTINGS_LOCALE));
|
||||
|
|
@ -142,6 +157,7 @@ watch(() => settings.sessionsGroup, save(SESSIONS_GROUP));
|
|||
watch(() => settings.sessionsType, save(SESSIONS_TYPE));
|
||||
watch(() => settings.solarAdjusted, saveBool(SETTINGS_SOLAR_ADJUSTED));
|
||||
watch(() => settings.loadpoints, saveJSON(LOADPOINTS), { deep: true });
|
||||
watch(() => settings.lastBatterySmartCostLimit, saveNumber(LAST_BATTERY_SMART_COST_LIMIT));
|
||||
|
||||
export default settings;
|
||||
|
||||
|
|
|
|||
|
|
@ -46,6 +46,7 @@ function updateTheme() {
|
|||
if ($html) {
|
||||
$html.classList.add("no-transitions");
|
||||
$html.classList.toggle("dark", theme === THEME.DARK);
|
||||
$html.setAttribute("data-bs-theme", theme!);
|
||||
window.setTimeout(function () {
|
||||
$html.classList.remove("no-transitions");
|
||||
}, 100);
|
||||
|
|
|
|||
|
|
@ -261,6 +261,8 @@ export interface UiLoadpoint extends Loadpoint {
|
|||
icon: string;
|
||||
order: number | null;
|
||||
visible: boolean;
|
||||
lastSmartCostLimit: number | undefined;
|
||||
lastSmartFeedInPriorityLimit: number | undefined;
|
||||
}
|
||||
|
||||
export enum THEME {
|
||||
|
|
|
|||
|
|
@ -26,6 +26,8 @@ export const convertToUiLoadpoints = (
|
|||
order: getLoadpointOrder(id),
|
||||
visible: isLoadpointVisible(id),
|
||||
sessionInfo: getLoadpointSessionInfo(id),
|
||||
lastSmartCostLimit: getLoadpointLastSmartCostLimit(id),
|
||||
lastSmartFeedInPriorityLimit: getLoadpointLastSmartFeedInPriorityLimit(id),
|
||||
};
|
||||
});
|
||||
|
||||
|
|
@ -56,10 +58,26 @@ export const getLoadpointSessionInfo = (id: string): SessionInfoKey | undefined
|
|||
return get(id).info;
|
||||
};
|
||||
|
||||
export const getLoadpointLastSmartCostLimit = (id: string): number | undefined => {
|
||||
return get(id).lastSmartCostLimit;
|
||||
};
|
||||
|
||||
export const setLoadpointSessionInfo = (id: string, value: SessionInfoKey) => {
|
||||
get(id).info = value;
|
||||
};
|
||||
|
||||
export const setLoadpointLastSmartCostLimit = (id: string, value: number) => {
|
||||
get(id).lastSmartCostLimit = value;
|
||||
};
|
||||
|
||||
export const getLoadpointLastSmartFeedInPriorityLimit = (id: string): number | undefined => {
|
||||
return get(id).lastSmartFeedInPriorityLimit;
|
||||
};
|
||||
|
||||
export const setLoadpointLastSmartFeedInPriorityLimit = (id: string, value: number) => {
|
||||
get(id).lastSmartFeedInPriorityLimit = value;
|
||||
};
|
||||
|
||||
export const resetLoadpointsOrder = () => {
|
||||
Object.keys(settings.loadpoints).forEach((id) => {
|
||||
if (settings.loadpoints[id]) {
|
||||
|
|
|
|||
|
|
@ -1165,6 +1165,7 @@
|
|||
"cleanTitle": "Sauberes Netzladen",
|
||||
"co2Label": "CO₂-Emission",
|
||||
"co2Limit": "CO₂-Grenze",
|
||||
"enable": "Grenze aktivieren",
|
||||
"loadpointDescription": "Aktiviert vorübergehendes Schnellladen im PV-Modus.",
|
||||
"modalTitle": "Smartes Netzladen",
|
||||
"none": "keine",
|
||||
|
|
|
|||
|
|
@ -1166,6 +1166,7 @@
|
|||
"cleanTitle": "Clean Grid Charging",
|
||||
"co2Label": "CO₂ emission",
|
||||
"co2Limit": "CO₂ limit",
|
||||
"enable": "Enable limit",
|
||||
"loadpointDescription": "Enables temporary fast-charging in solar mode.",
|
||||
"modalTitle": "Smart Grid Charging",
|
||||
"none": "none",
|
||||
|
|
|
|||
|
|
@ -19,7 +19,9 @@ test.describe("battery settings co2", async () => {
|
|||
await expectModalVisible(modal);
|
||||
|
||||
await modal.getByRole("link", { name: "Grid charging" }).click();
|
||||
await modal.getByLabel("Enable limit").check();
|
||||
await modal.getByLabel("CO₂ limit").selectOption({ label: "≤ 150 g/kWh" });
|
||||
await expect(modal.getByTestId("active-hours")).toHaveText(["Active time", "48 hr"].join(""));
|
||||
await expect(modal).toContainText("20 g – 150 g");
|
||||
await page.getByRole("button", { name: "Close" }).click();
|
||||
await expectModalHidden(modal);
|
||||
|
|
@ -27,6 +29,7 @@ test.describe("battery settings co2", async () => {
|
|||
await page.getByRole("button", { name: "grid charging active (≤ 150 g)" }).click();
|
||||
await expectModalVisible(modal);
|
||||
await modal.getByLabel("CO₂ limit").selectOption({ label: "≤ 10 g/kWh" });
|
||||
await expect(modal.getByTestId("active-hours")).toHaveText("Active time");
|
||||
await modal.getByRole("button", { name: "Close" }).click();
|
||||
await expectModalHidden(modal);
|
||||
await expect(modal).not.toBeVisible();
|
||||
|
|
|
|||
|
|
@ -47,7 +47,9 @@ test.describe("battery settings", async () => {
|
|||
await expectModalVisible(modal);
|
||||
|
||||
await modal.getByRole("link", { name: "Grid charging" }).click();
|
||||
await modal.getByLabel("Enable limit").check();
|
||||
await modal.getByLabel("Price limit").selectOption({ label: "≤ 50.0 ct/kWh" });
|
||||
await expect(modal.getByTestId("active-hours")).toHaveText(["Active time", "48 hr"].join(""));
|
||||
await expect(modal).toContainText("5.0 ct – 50.0 ct");
|
||||
await page.getByRole("button", { name: "Close" }).click();
|
||||
await expectModalHidden(modal);
|
||||
|
|
@ -55,6 +57,7 @@ test.describe("battery settings", async () => {
|
|||
await page.getByRole("button", { name: "grid charging active (≤ 50.0 ct)" }).click();
|
||||
await expectModalVisible(modal);
|
||||
await modal.getByLabel("Price limit").selectOption({ label: "≤ -10.0 ct/kWh" });
|
||||
await expect(modal.getByTestId("active-hours")).toHaveText("Active time");
|
||||
await modal.getByRole("button", { name: "Close" }).click();
|
||||
await expectModalHidden(modal);
|
||||
await expect(modal).not.toBeVisible();
|
||||
|
|
|
|||
|
|
@ -9,12 +9,14 @@ import {
|
|||
} from "./simulator";
|
||||
|
||||
test.use({ baseURL: baseUrl() });
|
||||
test.describe.configure({ mode: "parallel" });
|
||||
|
||||
test.beforeAll(async () => {
|
||||
test.beforeEach(async () => {
|
||||
await startSimulator();
|
||||
await start(simulatorConfig());
|
||||
});
|
||||
test.afterAll(async () => {
|
||||
|
||||
test.afterEach(async () => {
|
||||
await stop();
|
||||
await stopSimulator();
|
||||
});
|
||||
|
|
@ -36,26 +38,26 @@ test.describe("smart cost limit", async () => {
|
|||
test("price below limit", async ({ page }) => {
|
||||
await page.goto("/");
|
||||
await page.getByTestId("loadpoint-settings-button").nth(1).click();
|
||||
await expect(page.getByTestId("loadpoint-settings-modal")).toBeVisible();
|
||||
await page
|
||||
.getByTestId("loadpoint-settings-modal")
|
||||
.getByLabel("Price limit")
|
||||
.selectOption("≤ 40.0 ct/kWh");
|
||||
await page.getByTestId("loadpoint-settings-modal").getByLabel("Close").click();
|
||||
await expect(page.getByTestId("loadpoint-settings-modal")).not.toBeVisible();
|
||||
const modal = page.getByTestId("loadpoint-settings-modal");
|
||||
await expect(modal).toBeVisible();
|
||||
await modal.getByLabel("Enable limit").check();
|
||||
await modal.getByLabel("Price limit").selectOption("≤ 40.0 ct/kWh");
|
||||
await expect(modal.getByTestId("active-hours")).toHaveText(["Active time", "48 hr"].join(""));
|
||||
await modal.getByLabel("Close").click();
|
||||
await expect(modal).not.toBeVisible();
|
||||
await expect(page.getByTestId("vehicle-status-charger")).toHaveText("Charging…");
|
||||
await expect(page.getByTestId("vehicle-status-smartcost")).toHaveText(/[24]0\.0 ct ≤ 40\.0 ct/);
|
||||
});
|
||||
test("price above limit", async ({ page }) => {
|
||||
await page.goto("/");
|
||||
await page.getByTestId("loadpoint-settings-button").nth(1).click();
|
||||
await expect(page.getByTestId("loadpoint-settings-modal")).toBeVisible();
|
||||
await page
|
||||
.getByTestId("loadpoint-settings-modal")
|
||||
.getByLabel("Price limit")
|
||||
.selectOption("≤ 10.0 ct/kWh");
|
||||
await page.getByTestId("loadpoint-settings-modal").getByLabel("Close").click();
|
||||
await expect(page.getByTestId("loadpoint-settings-modal")).not.toBeVisible();
|
||||
const modal = page.getByTestId("loadpoint-settings-modal");
|
||||
await expect(modal).toBeVisible();
|
||||
await modal.getByLabel("Enable limit").check();
|
||||
await modal.getByLabel("Price limit").selectOption("≤ 10.0 ct/kWh");
|
||||
await expect(modal.getByTestId("active-hours")).toHaveText("Active time");
|
||||
await modal.getByLabel("Close").click();
|
||||
await expect(modal).not.toBeVisible();
|
||||
await expect(page.getByTestId("vehicle-status-charger")).toHaveText("Charging…");
|
||||
await expect(page.getByTestId("vehicle-status-smartcost")).toHaveText("≤ 10.0 ct");
|
||||
});
|
||||
|
|
|
|||
|
|
@ -5,12 +5,13 @@ import { expectModalHidden, expectModalVisible } from "./utils";
|
|||
const CONFIG = "smart-feedin.evcc.yaml";
|
||||
|
||||
test.use({ baseURL: baseUrl() });
|
||||
test.describe.configure({ mode: "parallel" });
|
||||
|
||||
test.beforeAll(async () => {
|
||||
test.beforeEach(async () => {
|
||||
await start(CONFIG);
|
||||
});
|
||||
|
||||
test.afterAll(async () => {
|
||||
test.afterEach(async () => {
|
||||
await stop();
|
||||
});
|
||||
|
||||
|
|
@ -28,7 +29,9 @@ test.describe("smart feed-in priority", async () => {
|
|||
await lp.getByTestId("loadpoint-settings-button").nth(1).click();
|
||||
const modal = page.getByTestId("loadpoint-settings-modal").first();
|
||||
await expectModalVisible(modal);
|
||||
await modal.getByLabel("Enable limit").check();
|
||||
await modal.getByLabel("Feed-in limit").selectOption("≥ 10.0 ct/kWh");
|
||||
await expect(modal.getByTestId("active-hours")).toHaveText(["Paused time", "48 hr"].join(""));
|
||||
await modal.getByLabel("Close").click();
|
||||
await expectModalHidden(modal);
|
||||
|
||||
|
|
@ -39,7 +42,7 @@ test.describe("smart feed-in priority", async () => {
|
|||
// remove limit
|
||||
await lp.getByTestId("loadpoint-settings-button").nth(1).click();
|
||||
await expectModalVisible(modal);
|
||||
await modal.getByLabel("Feed-in limit").selectOption("none");
|
||||
await modal.getByLabel("Enable limit").uncheck();
|
||||
await modal.getByLabel("Close").click();
|
||||
await expectModalHidden(modal);
|
||||
|
||||
|
|
@ -54,7 +57,9 @@ test.describe("smart feed-in priority", async () => {
|
|||
await lp1.getByTestId("loadpoint-settings-button").nth(1).click();
|
||||
const modal1 = page.getByTestId("loadpoint-settings-modal").first();
|
||||
await expectModalVisible(modal1);
|
||||
await modal1.getByLabel("Enable limit").check();
|
||||
await modal1.getByLabel("Feed-in limit").selectOption("≥ 10.0 ct/kWh");
|
||||
await expect(modal1.getByTestId("active-hours")).toHaveText(["Paused time", "48 hr"].join(""));
|
||||
await modal1.getByRole("button", { name: "Apply everywhere?" }).click();
|
||||
await modal1.getByLabel("Close").click();
|
||||
await expectModalHidden(modal1);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue