Battery Boost UI: disable button when battery on hold (#28489)
This commit is contained in:
parent
2499c09b94
commit
948dda081c
13 changed files with 132 additions and 39 deletions
|
|
@ -2,7 +2,7 @@
|
||||||
<button
|
<button
|
||||||
class="root position-relative"
|
class="root position-relative"
|
||||||
tabindex="0"
|
tabindex="0"
|
||||||
:class="{ active, belowLimit, full }"
|
:class="{ active, belowLimit, batteryHold, full }"
|
||||||
:style="{ '--soc': `${adjustedSoc}%` }"
|
:style="{ '--soc': `${adjustedSoc}%` }"
|
||||||
:disabled="disabled"
|
:disabled="disabled"
|
||||||
:aria-label="ariaLabel"
|
:aria-label="ariaLabel"
|
||||||
|
|
@ -17,17 +17,17 @@
|
||||||
<div class="progress-bar bg-primary progress-bar-striped progress-bar-animated"></div>
|
<div class="progress-bar bg-primary progress-bar-striped progress-bar-animated"></div>
|
||||||
</div>
|
</div>
|
||||||
<div class="icon-wrapper" :style="iconStyle">
|
<div class="icon-wrapper" :style="iconStyle">
|
||||||
<BatteryBoost :active="active && !belowLimit" />
|
<BatteryBoost :active="active && available" />
|
||||||
</div>
|
</div>
|
||||||
<div class="icon-wrapper text-white" :style="iconActiveStyle">
|
<div class="icon-wrapper text-white" :style="iconActiveStyle">
|
||||||
<BatteryBoost :active="active && !belowLimit" />
|
<BatteryBoost :active="active && available" />
|
||||||
</div>
|
</div>
|
||||||
</button>
|
</button>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { defineComponent, type PropType } from "vue";
|
import { defineComponent, type PropType } from "vue";
|
||||||
import { CHARGE_MODE, type Timeout } from "@/types/evcc";
|
import { BATTERY_MODE, CHARGE_MODE, type Timeout } from "@/types/evcc";
|
||||||
import BatteryBoost from "../MaterialIcon/BatteryBoost.vue";
|
import BatteryBoost from "../MaterialIcon/BatteryBoost.vue";
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
|
|
@ -40,6 +40,7 @@ export default defineComponent({
|
||||||
batteryBoostLimit: { type: Number, default: 100 },
|
batteryBoostLimit: { type: Number, default: 100 },
|
||||||
mode: String as PropType<CHARGE_MODE>,
|
mode: String as PropType<CHARGE_MODE>,
|
||||||
batterySoc: { type: Number, default: 0 },
|
batterySoc: { type: Number, default: 0 },
|
||||||
|
batteryMode: String as PropType<BATTERY_MODE>,
|
||||||
},
|
},
|
||||||
emits: ["updated", "status"],
|
emits: ["updated", "status"],
|
||||||
data() {
|
data() {
|
||||||
|
|
@ -66,6 +67,12 @@ export default defineComponent({
|
||||||
belowLimit(): boolean {
|
belowLimit(): boolean {
|
||||||
return this.batterySoc < this.batteryBoostLimit;
|
return this.batterySoc < this.batteryBoostLimit;
|
||||||
},
|
},
|
||||||
|
batteryHold(): boolean {
|
||||||
|
return this.batteryMode === BATTERY_MODE.HOLD;
|
||||||
|
},
|
||||||
|
available(): boolean {
|
||||||
|
return !this.belowLimit && !this.batteryHold;
|
||||||
|
},
|
||||||
iconStyle() {
|
iconStyle() {
|
||||||
return {
|
return {
|
||||||
clipPath: this.active ? `inset(0 0 calc(var(--soc)) 0)` : undefined,
|
clipPath: this.active ? `inset(0 0 calc(var(--soc)) 0)` : undefined,
|
||||||
|
|
@ -76,6 +83,7 @@ export default defineComponent({
|
||||||
},
|
},
|
||||||
ariaLabel(): string {
|
ariaLabel(): string {
|
||||||
const t = (key: string) => this.$t(`main.loadpointSettings.batteryBoost.${key}`);
|
const t = (key: string) => this.$t(`main.loadpointSettings.batteryBoost.${key}`);
|
||||||
|
if (this.batteryHold) return t("stateHold");
|
||||||
if (this.active) return t("stateActive");
|
if (this.active) return t("stateActive");
|
||||||
if (this.belowLimit) return t("stateBelowLimit");
|
if (this.belowLimit) return t("stateBelowLimit");
|
||||||
return t("stateReady");
|
return t("stateReady");
|
||||||
|
|
@ -105,6 +113,12 @@ export default defineComponent({
|
||||||
});
|
});
|
||||||
const newValue = !this.active;
|
const newValue = !this.active;
|
||||||
|
|
||||||
|
// battery hold: only show message, don't toggle
|
||||||
|
if (newValue && this.batteryHold) {
|
||||||
|
status("batteryBoostHold");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// below limit: only show message, don't toggle
|
// below limit: only show message, don't toggle
|
||||||
if (newValue && this.belowLimit) {
|
if (newValue && this.belowLimit) {
|
||||||
status("batteryBoostBelowLimit");
|
status("batteryBoostBelowLimit");
|
||||||
|
|
@ -157,7 +171,8 @@ export default defineComponent({
|
||||||
color: inherit;
|
color: inherit;
|
||||||
opacity: 0.25;
|
opacity: 0.25;
|
||||||
}
|
}
|
||||||
.root.belowLimit:not(:disabled) {
|
.root.belowLimit:not(:disabled),
|
||||||
|
.root.batteryHold:not(:disabled) {
|
||||||
opacity: 0.5;
|
opacity: 0.5;
|
||||||
}
|
}
|
||||||
.root:before,
|
.root:before,
|
||||||
|
|
|
||||||
|
|
@ -127,6 +127,7 @@ import type {
|
||||||
Vehicle,
|
Vehicle,
|
||||||
Forecast,
|
Forecast,
|
||||||
SMART_COST_TYPE,
|
SMART_COST_TYPE,
|
||||||
|
BATTERY_MODE,
|
||||||
} from "@/types/evcc";
|
} from "@/types/evcc";
|
||||||
import type { PlanStrategy } from "@/components/ChargingPlans/types";
|
import type { PlanStrategy } from "@/components/ChargingPlans/types";
|
||||||
|
|
||||||
|
|
@ -160,6 +161,7 @@ export default defineComponent({
|
||||||
batteryBoostLimit: { type: Number, default: 100 },
|
batteryBoostLimit: { type: Number, default: 100 },
|
||||||
batteryConfigured: Boolean,
|
batteryConfigured: Boolean,
|
||||||
batterySoc: Number,
|
batterySoc: Number,
|
||||||
|
batteryMode: String as PropType<BATTERY_MODE>,
|
||||||
|
|
||||||
// session
|
// session
|
||||||
sessionEnergy: Number,
|
sessionEnergy: Number,
|
||||||
|
|
|
||||||
|
|
@ -31,6 +31,7 @@
|
||||||
:pvConfigured="pvConfigured"
|
:pvConfigured="pvConfigured"
|
||||||
:batteryConfigured="batteryConfigured"
|
:batteryConfigured="batteryConfigured"
|
||||||
:batterySoc="batterySoc"
|
:batterySoc="batterySoc"
|
||||||
|
:batteryMode="batteryMode"
|
||||||
:forecast="forecast"
|
:forecast="forecast"
|
||||||
class="h-100"
|
class="h-100"
|
||||||
:class="{ 'loadpoint-unselected': !selected(loadpoint.id) }"
|
:class="{ 'loadpoint-unselected': !selected(loadpoint.id) }"
|
||||||
|
|
@ -67,7 +68,7 @@ import "@h2d2/shopicons/es/filled/lightning";
|
||||||
|
|
||||||
import Loadpoint from "./Loadpoint.vue";
|
import Loadpoint from "./Loadpoint.vue";
|
||||||
import { defineComponent, type PropType } from "vue";
|
import { defineComponent, type PropType } from "vue";
|
||||||
import type { UiLoadpoint, SMART_COST_TYPE, Timeout, Vehicle } from "@/types/evcc";
|
import type { UiLoadpoint, SMART_COST_TYPE, Timeout, Vehicle, BATTERY_MODE } from "@/types/evcc";
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
name: "Loadpoints",
|
name: "Loadpoints",
|
||||||
|
|
@ -87,6 +88,7 @@ export default defineComponent({
|
||||||
pvConfigured: Boolean,
|
pvConfigured: Boolean,
|
||||||
batteryConfigured: Boolean,
|
batteryConfigured: Boolean,
|
||||||
batterySoc: Number,
|
batterySoc: Number,
|
||||||
|
batteryMode: String as PropType<BATTERY_MODE>,
|
||||||
forecast: Object, // as PropType<Forecast>,
|
forecast: Object, // as PropType<Forecast>,
|
||||||
},
|
},
|
||||||
emits: ["id-changed"],
|
emits: ["id-changed"],
|
||||||
|
|
|
||||||
|
|
@ -69,6 +69,7 @@
|
||||||
:pvConfigured="pvConfigured"
|
:pvConfigured="pvConfigured"
|
||||||
:batteryConfigured="batteryConfigured"
|
:batteryConfigured="batteryConfigured"
|
||||||
:batterySoc="batterySoc"
|
:batterySoc="batterySoc"
|
||||||
|
:batteryMode="batteryMode"
|
||||||
:forecast="forecast"
|
:forecast="forecast"
|
||||||
:selectedId="selectedLoadpointId"
|
:selectedId="selectedLoadpointId"
|
||||||
@id-changed="selectedLoadpointChanged"
|
@id-changed="selectedLoadpointChanged"
|
||||||
|
|
@ -101,6 +102,7 @@ import type {
|
||||||
Sponsor,
|
Sponsor,
|
||||||
FatalError,
|
FatalError,
|
||||||
EvOpt,
|
EvOpt,
|
||||||
|
BATTERY_MODE,
|
||||||
} from "@/types/evcc";
|
} from "@/types/evcc";
|
||||||
import store from "@/store";
|
import store from "@/store";
|
||||||
import type { Grid } from "./types";
|
import type { Grid } from "./types";
|
||||||
|
|
@ -134,7 +136,7 @@ export default defineComponent({
|
||||||
batteryDischargeControl: Boolean,
|
batteryDischargeControl: Boolean,
|
||||||
batteryGridChargeLimit: { type: [Number, null] as PropType<number | null>, default: null },
|
batteryGridChargeLimit: { type: [Number, null] as PropType<number | null>, default: null },
|
||||||
batteryGridChargeActive: Boolean,
|
batteryGridChargeActive: Boolean,
|
||||||
batteryMode: String,
|
batteryMode: String as PropType<BATTERY_MODE>,
|
||||||
battery: { type: Object as PropType<Battery> },
|
battery: { type: Object as PropType<Battery> },
|
||||||
gridCurrents: Array,
|
gridCurrents: Array,
|
||||||
prioritySoc: Number,
|
prioritySoc: Number,
|
||||||
|
|
|
||||||
|
|
@ -88,7 +88,13 @@ import LimitSocSelect from "./LimitSocSelect.vue";
|
||||||
import LimitEnergySelect from "./LimitEnergySelect.vue";
|
import LimitEnergySelect from "./LimitEnergySelect.vue";
|
||||||
import { distanceUnit, distanceValue } from "@/units.ts";
|
import { distanceUnit, distanceValue } from "@/units.ts";
|
||||||
import { defineComponent, type PropType } from "vue";
|
import { defineComponent, type PropType } from "vue";
|
||||||
import { CHARGE_MODE, type Forecast, type VehicleStatus, type Vehicle } from "@/types/evcc";
|
import {
|
||||||
|
CHARGE_MODE,
|
||||||
|
type BATTERY_MODE,
|
||||||
|
type Forecast,
|
||||||
|
type VehicleStatus,
|
||||||
|
type Vehicle,
|
||||||
|
} from "@/types/evcc";
|
||||||
import type { PlanStrategy } from "@/components/ChargingPlans/types";
|
import type { PlanStrategy } from "@/components/ChargingPlans/types";
|
||||||
import BatteryBoostButton from "../Loadpoints/BatteryBoostButton.vue";
|
import BatteryBoostButton from "../Loadpoints/BatteryBoostButton.vue";
|
||||||
|
|
||||||
|
|
@ -121,6 +127,7 @@ export default defineComponent({
|
||||||
batteryBoostAvailable: Boolean,
|
batteryBoostAvailable: Boolean,
|
||||||
batteryBoostLimit: { type: Number, default: 100 },
|
batteryBoostLimit: { type: Number, default: 100 },
|
||||||
batterySoc: Number,
|
batterySoc: Number,
|
||||||
|
batteryMode: String as PropType<BATTERY_MODE>,
|
||||||
enabled: Boolean,
|
enabled: Boolean,
|
||||||
heating: Boolean,
|
heating: Boolean,
|
||||||
id: [String, Number],
|
id: [String, Number],
|
||||||
|
|
|
||||||
|
|
@ -78,6 +78,7 @@ export interface State {
|
||||||
system?: string;
|
system?: string;
|
||||||
timezone?: string;
|
timezone?: string;
|
||||||
battery?: Battery;
|
battery?: Battery;
|
||||||
|
batteryMode?: BATTERY_MODE;
|
||||||
pv?: Meter[];
|
pv?: Meter[];
|
||||||
aux?: Meter[];
|
aux?: Meter[];
|
||||||
ext?: Meter[];
|
ext?: Meter[];
|
||||||
|
|
@ -377,6 +378,13 @@ export enum CHARGE_MODE {
|
||||||
PV = "pv",
|
PV = "pv",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export enum BATTERY_MODE {
|
||||||
|
UNKNOWN = "unknown",
|
||||||
|
NORMAL = "normal",
|
||||||
|
HOLD = "hold",
|
||||||
|
CHARGE = "charge",
|
||||||
|
}
|
||||||
|
|
||||||
export enum PHASES {
|
export enum PHASES {
|
||||||
AUTO = 0,
|
AUTO = 0,
|
||||||
ONE_PHASE = 1,
|
ONE_PHASE = 1,
|
||||||
|
|
|
||||||
|
|
@ -1156,6 +1156,7 @@
|
||||||
"once": "Boost ist für diesen Ladevorgang aktiviert.",
|
"once": "Boost ist für diesen Ladevorgang aktiviert.",
|
||||||
"stateActive": "Batterie Boost aktiv",
|
"stateActive": "Batterie Boost aktiv",
|
||||||
"stateBelowLimit": "Batterie zu niedrig für Boost",
|
"stateBelowLimit": "Batterie zu niedrig für Boost",
|
||||||
|
"stateHold": "Batterie gesperrt",
|
||||||
"stateReady": "Batterie Boost bereit"
|
"stateReady": "Batterie Boost bereit"
|
||||||
},
|
},
|
||||||
"batteryUsage": "Hausbatterie",
|
"batteryUsage": "Hausbatterie",
|
||||||
|
|
@ -1265,6 +1266,7 @@
|
||||||
"batteryBoostBelowLimit": "Batterie zu niedrig für Boost.",
|
"batteryBoostBelowLimit": "Batterie zu niedrig für Boost.",
|
||||||
"batteryBoostDisabled": "Batterie Boost deaktiviert.",
|
"batteryBoostDisabled": "Batterie Boost deaktiviert.",
|
||||||
"batteryBoostEnabled": "Boost bis Batterie bei {limit}.",
|
"batteryBoostEnabled": "Boost bis Batterie bei {limit}.",
|
||||||
|
"batteryBoostHold": "Batterie gesperrt. Boost nicht verfügbar.",
|
||||||
"charging": "Ladevorgang aktiv …",
|
"charging": "Ladevorgang aktiv …",
|
||||||
"cheapEnergyCharging": "Günstige Energie verfügbar.",
|
"cheapEnergyCharging": "Günstige Energie verfügbar.",
|
||||||
"cheapEnergyNextStart": "Günstige Energie in {duration}.",
|
"cheapEnergyNextStart": "Günstige Energie in {duration}.",
|
||||||
|
|
|
||||||
|
|
@ -1159,6 +1159,7 @@
|
||||||
"once": "Boost active for this charging session.",
|
"once": "Boost active for this charging session.",
|
||||||
"stateActive": "Battery boost active",
|
"stateActive": "Battery boost active",
|
||||||
"stateBelowLimit": "Battery too low for boost",
|
"stateBelowLimit": "Battery too low for boost",
|
||||||
|
"stateHold": "Battery locked",
|
||||||
"stateReady": "Battery boost ready"
|
"stateReady": "Battery boost ready"
|
||||||
},
|
},
|
||||||
"batteryUsage": "Home Battery",
|
"batteryUsage": "Home Battery",
|
||||||
|
|
@ -1268,6 +1269,7 @@
|
||||||
"batteryBoostBelowLimit": "Battery too low for boost.",
|
"batteryBoostBelowLimit": "Battery too low for boost.",
|
||||||
"batteryBoostDisabled": "Battery boost disabled.",
|
"batteryBoostDisabled": "Battery boost disabled.",
|
||||||
"batteryBoostEnabled": "Boost until battery at {limit}.",
|
"batteryBoostEnabled": "Boost until battery at {limit}.",
|
||||||
|
"batteryBoostHold": "Battery locked. Boost not available.",
|
||||||
"charging": "Charging…",
|
"charging": "Charging…",
|
||||||
"cheapEnergyCharging": "Cheap energy available.",
|
"cheapEnergyCharging": "Cheap energy available.",
|
||||||
"cheapEnergyNextStart": "Cheap energy in {duration}.",
|
"cheapEnergyNextStart": "Cheap energy in {duration}.",
|
||||||
|
|
|
||||||
|
|
@ -1089,7 +1089,7 @@
|
||||||
},
|
},
|
||||||
"/loadpoints/{id}/batteryboost/{enable}": {
|
"/loadpoints/{id}/batteryboost/{enable}": {
|
||||||
"post": {
|
"post": {
|
||||||
"description": "Enable or disable battery boost. When active, the maximum available home battery power is added until the home battery is drained to configured SoC limit.",
|
"description": "Enable or disable battery boost. When active, the maximum available home battery power is added until the home battery is drained to configured SoC limit. Note: boost will not work while the battery is on hold (e.g. during fast charging or planned charging with discharge prevention enabled).",
|
||||||
"externalDocs": {
|
"externalDocs": {
|
||||||
"url": "https://docs.evcc.io/en/docs/features/battery#battery-boost"
|
"url": "https://docs.evcc.io/en/docs/features/battery#battery-boost"
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -482,7 +482,7 @@ call removeLoadpointVehicle {
|
||||||
|
|
||||||
## setLoadpointBatteryBoost
|
## setLoadpointBatteryBoost
|
||||||
|
|
||||||
Enable or disable battery boost. When active, the maximum available home battery power is added until the home battery is drained to configured SoC limit.
|
Enable or disable battery boost. When active, the maximum available home battery power is added until the home battery is drained to configured SoC limit. Note: boost will not work while the battery is on hold (e.g. during fast charging or planned charging with discharge prevention enabled).
|
||||||
|
|
||||||
**Tags:** loadpoints
|
**Tags:** loadpoints
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -192,7 +192,7 @@ paths:
|
||||||
post:
|
post:
|
||||||
operationId: setLoadpointBatteryBoost
|
operationId: setLoadpointBatteryBoost
|
||||||
summary: Set battery boost
|
summary: Set battery boost
|
||||||
description: "Enable or disable battery boost. When active, the maximum available home battery power is added until the home battery is drained to configured SoC limit."
|
description: "Enable or disable battery boost. When active, the maximum available home battery power is added until the home battery is drained to configured SoC limit. Note: boost will not work while the battery is on hold (e.g. during fast charging or planned charging with discharge prevention enabled)."
|
||||||
externalDocs:
|
externalDocs:
|
||||||
url: https://docs.evcc.io/en/docs/features/battery#battery-boost
|
url: https://docs.evcc.io/en/docs/features/battery#battery-boost
|
||||||
tags:
|
tags:
|
||||||
|
|
|
||||||
|
|
@ -37,6 +37,9 @@ loadpoints:
|
||||||
- title: Carport
|
- title: Carport
|
||||||
charger: charger
|
charger: charger
|
||||||
mode: now
|
mode: now
|
||||||
|
- title: Garage
|
||||||
|
charger: charger2
|
||||||
|
mode: off
|
||||||
|
|
||||||
chargers:
|
chargers:
|
||||||
- name: charger
|
- name: charger
|
||||||
|
|
@ -56,6 +59,23 @@ chargers:
|
||||||
source: js
|
source: js
|
||||||
script: |
|
script: |
|
||||||
16
|
16
|
||||||
|
- name: charger2
|
||||||
|
type: custom
|
||||||
|
enable:
|
||||||
|
source: js
|
||||||
|
script:
|
||||||
|
enabled:
|
||||||
|
source: js
|
||||||
|
script: |
|
||||||
|
true
|
||||||
|
status:
|
||||||
|
source: js
|
||||||
|
script: |
|
||||||
|
"C"
|
||||||
|
maxcurrent:
|
||||||
|
source: js
|
||||||
|
script: |
|
||||||
|
16
|
||||||
|
|
||||||
tariffs:
|
tariffs:
|
||||||
currency: EUR
|
currency: EUR
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,13 @@
|
||||||
import { test, expect } from "@playwright/test";
|
import { test, expect } from "@playwright/test";
|
||||||
import { start, stop, restart, baseUrl } from "./evcc";
|
import { start, stop, restart, baseUrl } from "./evcc";
|
||||||
import { expectModalHidden, newLoadpoint, addDemoCharger, ChargerStatus } from "./utils";
|
import {
|
||||||
|
expectModalHidden,
|
||||||
|
expectModalVisible,
|
||||||
|
newLoadpoint,
|
||||||
|
addDemoCharger,
|
||||||
|
ChargerStatus,
|
||||||
|
openTopNavigation,
|
||||||
|
} from "./utils";
|
||||||
test.use({ baseURL: baseUrl() });
|
test.use({ baseURL: baseUrl() });
|
||||||
|
|
||||||
const CONFIG_BATTERY = "battery-settings.evcc.yaml";
|
const CONFIG_BATTERY = "battery-settings.evcc.yaml";
|
||||||
|
|
@ -13,15 +20,12 @@ test.describe("boost", async () => {
|
||||||
test("activate and deactivate boost in solar mode", async ({ page }) => {
|
test("activate and deactivate boost in solar mode", async ({ page }) => {
|
||||||
await start(CONFIG_BATTERY);
|
await start(CONFIG_BATTERY);
|
||||||
await page.goto("/");
|
await page.goto("/");
|
||||||
const boostButton = page.getByTestId("battery-boost-button");
|
const lp = page.getByTestId("loadpoint").first();
|
||||||
|
const boostButton = lp.getByTestId("battery-boost-button");
|
||||||
await expect(boostButton).not.toBeVisible();
|
await expect(boostButton).not.toBeVisible();
|
||||||
await page
|
await lp.getByTestId("mode").getByRole("button", { name: "Solar", exact: true }).click();
|
||||||
.getByTestId("mode")
|
await lp.getByTestId("loadpoint-settings-button").last().click();
|
||||||
.first()
|
const modal = page.getByTestId("loadpoint-settings-modal").first();
|
||||||
.getByRole("button", { name: "Solar", exact: true })
|
|
||||||
.click();
|
|
||||||
await page.getByTestId("loadpoint-settings-button").nth(1).click();
|
|
||||||
const modal = page.getByTestId("loadpoint-settings-modal");
|
|
||||||
await modal.getByTestId("battery-boost-limit").selectOption("20 %");
|
await modal.getByTestId("battery-boost-limit").selectOption("20 %");
|
||||||
await expect(modal.getByTestId("battery-boost")).toContainText(
|
await expect(modal.getByTestId("battery-boost")).toContainText(
|
||||||
"Allow fast charging from home battery until it's drained to 20%."
|
"Allow fast charging from home battery until it's drained to 20%."
|
||||||
|
|
@ -41,14 +45,11 @@ test.describe("boost", async () => {
|
||||||
test("battery too low for boost when limit above soc", async ({ page }) => {
|
test("battery too low for boost when limit above soc", async ({ page }) => {
|
||||||
await start(CONFIG_BATTERY);
|
await start(CONFIG_BATTERY);
|
||||||
await page.goto("/");
|
await page.goto("/");
|
||||||
const boostButton = page.getByTestId("battery-boost-button");
|
const lp = page.getByTestId("loadpoint").first();
|
||||||
await page
|
const boostButton = lp.getByTestId("battery-boost-button");
|
||||||
.getByTestId("mode")
|
await lp.getByTestId("mode").getByRole("button", { name: "Solar", exact: true }).click();
|
||||||
.first()
|
await lp.getByTestId("loadpoint-settings-button").last().click();
|
||||||
.getByRole("button", { name: "Solar", exact: true })
|
const modal = page.getByTestId("loadpoint-settings-modal").first();
|
||||||
.click();
|
|
||||||
await page.getByTestId("loadpoint-settings-button").nth(1).click();
|
|
||||||
const modal = page.getByTestId("loadpoint-settings-modal");
|
|
||||||
await modal.getByTestId("battery-boost-limit").selectOption("90 %");
|
await modal.getByTestId("battery-boost-limit").selectOption("90 %");
|
||||||
await expect(modal.getByTestId("battery-boost")).toContainText("drained to 90%");
|
await expect(modal.getByTestId("battery-boost")).toContainText("drained to 90%");
|
||||||
await page.waitForLoadState("networkidle");
|
await page.waitForLoadState("networkidle");
|
||||||
|
|
@ -61,29 +62,61 @@ test.describe("boost", async () => {
|
||||||
test("boost button disabled in fast mode", async ({ page }) => {
|
test("boost button disabled in fast mode", async ({ page }) => {
|
||||||
await start(CONFIG_BATTERY);
|
await start(CONFIG_BATTERY);
|
||||||
await page.goto("/");
|
await page.goto("/");
|
||||||
const boostButton = page.getByTestId("battery-boost-button");
|
const lp = page.getByTestId("loadpoint").first();
|
||||||
|
const boostButton = lp.getByTestId("battery-boost-button");
|
||||||
// set a boost limit in solar mode so the boost button appears
|
// set a boost limit in solar mode so the boost button appears
|
||||||
await page
|
await lp.getByTestId("mode").getByRole("button", { name: "Solar", exact: true }).click();
|
||||||
.getByTestId("mode")
|
await lp.getByTestId("loadpoint-settings-button").last().click();
|
||||||
.first()
|
const modal = page.getByTestId("loadpoint-settings-modal").first();
|
||||||
.getByRole("button", { name: "Solar", exact: true })
|
|
||||||
.click();
|
|
||||||
await page.getByTestId("loadpoint-settings-button").nth(1).click();
|
|
||||||
const modal = page.getByTestId("loadpoint-settings-modal");
|
|
||||||
await modal.getByTestId("battery-boost-limit").selectOption("20 %");
|
await modal.getByTestId("battery-boost-limit").selectOption("20 %");
|
||||||
await expect(modal.getByTestId("battery-boost")).toContainText("drained to 20%");
|
await expect(modal.getByTestId("battery-boost")).toContainText("drained to 20%");
|
||||||
await page.waitForLoadState("networkidle");
|
await page.waitForLoadState("networkidle");
|
||||||
await modal.getByLabel("Close").click();
|
await modal.getByLabel("Close").click();
|
||||||
await expectModalHidden(modal);
|
await expectModalHidden(modal);
|
||||||
// switch to fast mode and verify boost button is disabled
|
// switch to fast mode and verify boost button is disabled
|
||||||
await page.getByTestId("mode").first().getByRole("button", { name: "Fast" }).click();
|
await lp.getByTestId("mode").getByRole("button", { name: "Fast" }).click();
|
||||||
await expect(boostButton).toBeDisabled();
|
await expect(boostButton).toBeDisabled();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("boost button disabled when battery is on hold", async ({ page }) => {
|
||||||
|
await start(CONFIG_BATTERY);
|
||||||
|
await page.goto("/");
|
||||||
|
|
||||||
|
// LP1: set solar mode, configure boost limit
|
||||||
|
const lp1 = page.getByTestId("loadpoint").first();
|
||||||
|
await lp1.getByTestId("mode").getByRole("button", { name: "Solar", exact: true }).click();
|
||||||
|
await lp1.getByTestId("loadpoint-settings-button").last().click();
|
||||||
|
const modal = page.getByTestId("loadpoint-settings-modal").first();
|
||||||
|
await modal.getByTestId("battery-boost-limit").selectOption("0 %");
|
||||||
|
await modal.getByLabel("Close").click();
|
||||||
|
await expectModalHidden(modal);
|
||||||
|
|
||||||
|
// enable "Prevent discharge in fast mode"
|
||||||
|
await openTopNavigation(page);
|
||||||
|
await page.getByTestId("topnavigation-battery").click();
|
||||||
|
const batteryModal = page.getByTestId("battery-settings-modal");
|
||||||
|
await expectModalVisible(batteryModal);
|
||||||
|
await batteryModal.getByLabel("Prevent discharge in fast mode and planned charging.").check();
|
||||||
|
await page.getByRole("button", { name: "Close" }).click();
|
||||||
|
await expectModalHidden(batteryModal);
|
||||||
|
|
||||||
|
// LP2: switch to fast mode → triggers global battery hold
|
||||||
|
const lp2 = page.getByTestId("loadpoint").nth(1);
|
||||||
|
await lp2.getByTestId("mode").getByRole("button", { name: "Fast" }).click();
|
||||||
|
|
||||||
|
// LP1: boost button should show hold state
|
||||||
|
const boostButton = lp1.getByTestId("battery-boost-button");
|
||||||
|
await expect(boostButton).toHaveAttribute("aria-label", "Battery locked");
|
||||||
|
|
||||||
|
// clicking should not change state
|
||||||
|
await boostButton.click();
|
||||||
|
await expect(boostButton).toHaveAttribute("aria-label", "Battery locked");
|
||||||
|
});
|
||||||
|
|
||||||
test("boost default for ui-created loadpoint", async ({ page }) => {
|
test("boost default for ui-created loadpoint", async ({ page }) => {
|
||||||
await start(CONFIG_BATTERY);
|
await start(CONFIG_BATTERY);
|
||||||
|
|
||||||
// create a second loadpoint
|
// create a third loadpoint
|
||||||
await page.goto("/#/config");
|
await page.goto("/#/config");
|
||||||
await newLoadpoint(page, "New Charger");
|
await newLoadpoint(page, "New Charger");
|
||||||
await addDemoCharger(page, undefined, ChargerStatus.Connected);
|
await addDemoCharger(page, undefined, ChargerStatus.Connected);
|
||||||
|
|
@ -94,7 +127,7 @@ test.describe("boost", async () => {
|
||||||
// restart evcc to apply new loadpoint
|
// restart evcc to apply new loadpoint
|
||||||
await restart(CONFIG_BATTERY);
|
await restart(CONFIG_BATTERY);
|
||||||
await page.goto("/");
|
await page.goto("/");
|
||||||
await expect(page.getByTestId("loadpoint")).toHaveCount(2);
|
await expect(page.getByTestId("loadpoint")).toHaveCount(3);
|
||||||
|
|
||||||
const lp = page.getByTestId("loadpoint").last();
|
const lp = page.getByTestId("loadpoint").last();
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue