Energyflow UI: add price/co2 toggle (#25717)

This commit is contained in:
Michael Geers 2025-12-01 16:52:42 +01:00 • committed by GitHub
parent bdcacb8374
commit 25382accc7
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 84 additions and 28 deletions

View file

@ -140,14 +140,16 @@
</template>
</EnergyflowEntry>
<EnergyflowEntry
:key="`grid-${showCo2}`"
:name="$t('main.energyflow.gridImport')"
icon="powersupply"
:power="gridImport"
:powerUnit="powerUnit"
:details="detailsValue(tariffGrid, tariffCo2)"
:detailsFmt="detailsFmt"
:detailsTooltip="detailsTooltip(tariffGrid, tariffCo2)"
:detailsClickable="hasPriceAndCo2"
data-testid="energyflow-entry-gridimport"
@details-clicked="toggleCo2"
/>
</div>
</div>
@ -163,15 +165,17 @@
<div>
<EnergyflowEntry
v-if="pvPossible"
:key="`home-${showCo2}`"
:name="$t('main.energyflow.homePower')"
icon="home"
:power="homePower"
:powerUnit="powerUnit"
:details="detailsValue(tariffPriceHome, tariffCo2Home)"
:detailsFmt="detailsFmt"
:detailsTooltip="detailsTooltip(tariffPriceHome, tariffCo2Home)"
:detailsClickable="hasPriceAndCo2"
data-testid="energyflow-entry-home"
:expanded="consumersExpanded"
@details-clicked="toggleCo2"
@toggle="toggleConsumers"
>
<template v-if="consumers.length > 0" #expanded>
@ -188,6 +192,7 @@
</template>
</EnergyflowEntry>
<EnergyflowEntry
:key="`loadpoints-${showCo2}`"
:name="loadpointsLabel"
icon="vehicle"
:iconProps="{ names: vehicleIcons }"
@ -199,11 +204,10 @@
: undefined
"
:detailsFmt="detailsFmt"
:detailsTooltip="
detailsTooltip(tariffPriceLoadpoints, tariffCo2Loadpoints)
"
:detailsClickable="hasPriceAndCo2"
data-testid="energyflow-entry-loadpoints"
:expanded="loadpointsExpanded"
@details-clicked="toggleCo2"
@toggle="toggleLoadpoints"
>
<template v-if="activeLoadpointsCount > 0" #expanded>
@ -277,14 +281,16 @@
</EnergyflowEntry>
<EnergyflowEntry
v-if="pvPossible"
:key="`export-${showCo2}`"
:name="$t('main.energyflow.pvExport')"
icon="powersupply"
:power="pvExport"
:powerUnit="powerUnit"
:details="detailsValue(-tariffFeedIn)"
:detailsFmt="detailsFmt"
:detailsTooltip="detailsTooltip(-tariffFeedIn)"
:detailsClickable="hasPriceAndCo2"
data-testid="energyflow-entry-gridexport"
@details-clicked="toggleCo2"
/>
</div>
</div>
@ -356,6 +362,21 @@ export default defineComponent({
return { detailsOpen: false, detailsCompleteHeight: null as number | null, ready: false };
},
computed: {
showCo2() {
if (this.hasCo2 && !this.hasPrice) {
return true;
}
return settings.energyflowCo2;
},
hasPrice() {
return this.tariffGrid !== undefined;
},
hasCo2() {
return this.tariffCo2 !== undefined;
},
hasPriceAndCo2() {
return this.hasPrice && this.hasCo2;
},
gridImport() {
return Math.max(0, this.gridPower);
},
@ -434,14 +455,14 @@ export default defineComponent({
fmtLoadpointTemp() {
return (temp: number) => this.fmtTemperature(temp);
},
co2Available() {
smartCostCo2() {
return this.smartCostType === SMART_COST_TYPE.CO2;
},
pvPossible() {
return this.pvConfigured || this.gridConfigured;
},
batteryGridChargeNow() {
if (this.co2Available) {
if (this.smartCostCo2) {
return this.fmtCo2Short(this.tariffCo2);
}
return this.fmtPricePerKWh(this.tariffGrid, this.currency, true);
@ -455,7 +476,7 @@ export default defineComponent({
if (!this.batteryGridChargeLimitSet) {
return;
}
if (this.co2Available) {
if (this.smartCostCo2) {
return this.fmtCo2Short(this.batteryGridChargeLimit);
}
return this.fmtPricePerKWh(this.batteryGridChargeLimit, this.currency, true);
@ -532,27 +553,16 @@ export default defineComponent({
window.removeEventListener("resize", this.updateHeight);
},
methods: {
detailsTooltip(price?: number, co2?: number) {
const result = [];
if (co2 !== undefined) {
result.push(`${this.fmtCo2Long(co2)}`);
}
if (price !== undefined) {
result.push(`${this.fmtPricePerKWh(price, this.currency)}`);
}
return result;
},
detailsValue(price?: number, co2?: number) {
if (this.co2Available) {
return co2;
}
return price;
return this.showCo2 ? co2 : price;
},
detailsFmt(value: number) {
if (this.co2Available) {
return this.fmtCo2Short(value);
}
return this.fmtPricePerKWh(value, this.currency, true);
return this.showCo2
? this.fmtCo2Short(value)
: this.fmtPricePerKWh(value, this.currency, true);
},
toggleCo2() {
settings.energyflowCo2 = !settings.energyflowCo2;
},
forecastFmt(value: number) {
if (typeof value !== "number") return "";

View file

@ -30,7 +30,7 @@
<span class="text-end text-nowrap ps-1 fw-bold d-flex align-items-center">
<div
ref="details"
class="fw-normal d-flex align-items-center"
class="fw-normal d-flex align-items-center user-select-none"
:class="{
'text-decoration-underline': detailsClickable,
'evcc-gray': detailsInactive,

View file

@ -8,6 +8,7 @@ const SETTINGS_UNIT = "settings_unit";
const SETTINGS_12H_FORMAT = "settings_12h_format";
const SETTINGS_HIDDEN_FEATURES = "settings_hidden_features";
const SETTINGS_ENERGYFLOW_DETAILS = "settings_energyflow_details";
const SETTINGS_ENERGYFLOW_CO2 = "settings_energyflow_co2";
const SETTINGS_ENERGYFLOW_PV = "settings_energyflow_pv";
const SETTINGS_ENERGYFLOW_BATTERY = "settings_energyflow_battery";
const SETTINGS_ENERGYFLOW_LOADPOINTS = "settings_energyflow_loadpoints";
@ -105,6 +106,7 @@ export interface Settings {
is12hFormat: boolean;
hiddenFeatures: boolean;
energyflowDetails: boolean;
energyflowCo2: boolean;
energyflowPv: boolean;
energyflowBattery: boolean;
energyflowLoadpoints: boolean;
@ -126,6 +128,7 @@ const settings: Settings = reactive({
is12hFormat: readBool(SETTINGS_12H_FORMAT),
hiddenFeatures: readBool(SETTINGS_HIDDEN_FEATURES),
energyflowDetails: readBool(SETTINGS_ENERGYFLOW_DETAILS),
energyflowCo2: readBool(SETTINGS_ENERGYFLOW_CO2),
energyflowPv: readBool(SETTINGS_ENERGYFLOW_PV),
energyflowBattery: readBool(SETTINGS_ENERGYFLOW_BATTERY),
energyflowLoadpoints: readBool(SETTINGS_ENERGYFLOW_LOADPOINTS),
@ -146,6 +149,7 @@ watch(() => settings.unit, save(SETTINGS_UNIT));
watch(() => settings.is12hFormat, saveBool(SETTINGS_12H_FORMAT));
watch(() => settings.hiddenFeatures, saveBool(SETTINGS_HIDDEN_FEATURES));
watch(() => settings.energyflowDetails, saveBool(SETTINGS_ENERGYFLOW_DETAILS));
watch(() => settings.energyflowCo2, saveBool(SETTINGS_ENERGYFLOW_CO2));
watch(() => settings.energyflowPv, saveBool(SETTINGS_ENERGYFLOW_PV));
watch(() => settings.energyflowBattery, saveBool(SETTINGS_ENERGYFLOW_BATTERY));
watch(() => settings.energyflowLoadpoints, saveBool(SETTINGS_ENERGYFLOW_LOADPOINTS));

42
tests/energyflow.spec.ts Normal file
View file

@ -0,0 +1,42 @@
import { test, expect } from "@playwright/test";
import { start, stop, baseUrl } from "./evcc";
const CONFIG = "config-with-tariffs.evcc.yaml";
test.use({ baseURL: baseUrl() });
test.beforeAll(async () => {
await start(CONFIG);
});
test.afterAll(async () => {
await stop();
});
test.beforeEach(async ({ page }) => {
await page.goto("/");
});
test("tariff toggle", async ({ page }) => {
const energyflow = page.getByTestId("energyflow");
await energyflow.click();
const details = page
.getByTestId("energyflow-entry-gridimport")
.getByTestId("energyflow-entry-details");
// check price
await expect(details).toHaveText("30.0 öre");
// toggle to co2
await details.click();
await expect(details).toHaveText("300 g");
// reload page and verify persistence
await page.reload();
await expect(details).toHaveText("300 g");
// toggle back to price
await details.click();
await expect(details).toHaveText("30.0 öre");
});