Energyflow UI: add price/co2 toggle (#25717)
This commit is contained in:
parent
bdcacb8374
commit
25382accc7
4 changed files with 84 additions and 28 deletions
|
|
@ -140,14 +140,16 @@
|
||||||
</template>
|
</template>
|
||||||
</EnergyflowEntry>
|
</EnergyflowEntry>
|
||||||
<EnergyflowEntry
|
<EnergyflowEntry
|
||||||
|
:key="`grid-${showCo2}`"
|
||||||
:name="$t('main.energyflow.gridImport')"
|
:name="$t('main.energyflow.gridImport')"
|
||||||
icon="powersupply"
|
icon="powersupply"
|
||||||
:power="gridImport"
|
:power="gridImport"
|
||||||
:powerUnit="powerUnit"
|
:powerUnit="powerUnit"
|
||||||
:details="detailsValue(tariffGrid, tariffCo2)"
|
:details="detailsValue(tariffGrid, tariffCo2)"
|
||||||
:detailsFmt="detailsFmt"
|
:detailsFmt="detailsFmt"
|
||||||
:detailsTooltip="detailsTooltip(tariffGrid, tariffCo2)"
|
:detailsClickable="hasPriceAndCo2"
|
||||||
data-testid="energyflow-entry-gridimport"
|
data-testid="energyflow-entry-gridimport"
|
||||||
|
@details-clicked="toggleCo2"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -163,15 +165,17 @@
|
||||||
<div>
|
<div>
|
||||||
<EnergyflowEntry
|
<EnergyflowEntry
|
||||||
v-if="pvPossible"
|
v-if="pvPossible"
|
||||||
|
:key="`home-${showCo2}`"
|
||||||
:name="$t('main.energyflow.homePower')"
|
:name="$t('main.energyflow.homePower')"
|
||||||
icon="home"
|
icon="home"
|
||||||
:power="homePower"
|
:power="homePower"
|
||||||
:powerUnit="powerUnit"
|
:powerUnit="powerUnit"
|
||||||
:details="detailsValue(tariffPriceHome, tariffCo2Home)"
|
:details="detailsValue(tariffPriceHome, tariffCo2Home)"
|
||||||
:detailsFmt="detailsFmt"
|
:detailsFmt="detailsFmt"
|
||||||
:detailsTooltip="detailsTooltip(tariffPriceHome, tariffCo2Home)"
|
:detailsClickable="hasPriceAndCo2"
|
||||||
data-testid="energyflow-entry-home"
|
data-testid="energyflow-entry-home"
|
||||||
:expanded="consumersExpanded"
|
:expanded="consumersExpanded"
|
||||||
|
@details-clicked="toggleCo2"
|
||||||
@toggle="toggleConsumers"
|
@toggle="toggleConsumers"
|
||||||
>
|
>
|
||||||
<template v-if="consumers.length > 0" #expanded>
|
<template v-if="consumers.length > 0" #expanded>
|
||||||
|
|
@ -188,6 +192,7 @@
|
||||||
</template>
|
</template>
|
||||||
</EnergyflowEntry>
|
</EnergyflowEntry>
|
||||||
<EnergyflowEntry
|
<EnergyflowEntry
|
||||||
|
:key="`loadpoints-${showCo2}`"
|
||||||
:name="loadpointsLabel"
|
:name="loadpointsLabel"
|
||||||
icon="vehicle"
|
icon="vehicle"
|
||||||
:iconProps="{ names: vehicleIcons }"
|
:iconProps="{ names: vehicleIcons }"
|
||||||
|
|
@ -199,11 +204,10 @@
|
||||||
: undefined
|
: undefined
|
||||||
"
|
"
|
||||||
:detailsFmt="detailsFmt"
|
:detailsFmt="detailsFmt"
|
||||||
:detailsTooltip="
|
:detailsClickable="hasPriceAndCo2"
|
||||||
detailsTooltip(tariffPriceLoadpoints, tariffCo2Loadpoints)
|
|
||||||
"
|
|
||||||
data-testid="energyflow-entry-loadpoints"
|
data-testid="energyflow-entry-loadpoints"
|
||||||
:expanded="loadpointsExpanded"
|
:expanded="loadpointsExpanded"
|
||||||
|
@details-clicked="toggleCo2"
|
||||||
@toggle="toggleLoadpoints"
|
@toggle="toggleLoadpoints"
|
||||||
>
|
>
|
||||||
<template v-if="activeLoadpointsCount > 0" #expanded>
|
<template v-if="activeLoadpointsCount > 0" #expanded>
|
||||||
|
|
@ -277,14 +281,16 @@
|
||||||
</EnergyflowEntry>
|
</EnergyflowEntry>
|
||||||
<EnergyflowEntry
|
<EnergyflowEntry
|
||||||
v-if="pvPossible"
|
v-if="pvPossible"
|
||||||
|
:key="`export-${showCo2}`"
|
||||||
:name="$t('main.energyflow.pvExport')"
|
:name="$t('main.energyflow.pvExport')"
|
||||||
icon="powersupply"
|
icon="powersupply"
|
||||||
:power="pvExport"
|
:power="pvExport"
|
||||||
:powerUnit="powerUnit"
|
:powerUnit="powerUnit"
|
||||||
:details="detailsValue(-tariffFeedIn)"
|
:details="detailsValue(-tariffFeedIn)"
|
||||||
:detailsFmt="detailsFmt"
|
:detailsFmt="detailsFmt"
|
||||||
:detailsTooltip="detailsTooltip(-tariffFeedIn)"
|
:detailsClickable="hasPriceAndCo2"
|
||||||
data-testid="energyflow-entry-gridexport"
|
data-testid="energyflow-entry-gridexport"
|
||||||
|
@details-clicked="toggleCo2"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -356,6 +362,21 @@ export default defineComponent({
|
||||||
return { detailsOpen: false, detailsCompleteHeight: null as number | null, ready: false };
|
return { detailsOpen: false, detailsCompleteHeight: null as number | null, ready: false };
|
||||||
},
|
},
|
||||||
computed: {
|
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() {
|
gridImport() {
|
||||||
return Math.max(0, this.gridPower);
|
return Math.max(0, this.gridPower);
|
||||||
},
|
},
|
||||||
|
|
@ -434,14 +455,14 @@ export default defineComponent({
|
||||||
fmtLoadpointTemp() {
|
fmtLoadpointTemp() {
|
||||||
return (temp: number) => this.fmtTemperature(temp);
|
return (temp: number) => this.fmtTemperature(temp);
|
||||||
},
|
},
|
||||||
co2Available() {
|
smartCostCo2() {
|
||||||
return this.smartCostType === SMART_COST_TYPE.CO2;
|
return this.smartCostType === SMART_COST_TYPE.CO2;
|
||||||
},
|
},
|
||||||
pvPossible() {
|
pvPossible() {
|
||||||
return this.pvConfigured || this.gridConfigured;
|
return this.pvConfigured || this.gridConfigured;
|
||||||
},
|
},
|
||||||
batteryGridChargeNow() {
|
batteryGridChargeNow() {
|
||||||
if (this.co2Available) {
|
if (this.smartCostCo2) {
|
||||||
return this.fmtCo2Short(this.tariffCo2);
|
return this.fmtCo2Short(this.tariffCo2);
|
||||||
}
|
}
|
||||||
return this.fmtPricePerKWh(this.tariffGrid, this.currency, true);
|
return this.fmtPricePerKWh(this.tariffGrid, this.currency, true);
|
||||||
|
|
@ -455,7 +476,7 @@ export default defineComponent({
|
||||||
if (!this.batteryGridChargeLimitSet) {
|
if (!this.batteryGridChargeLimitSet) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (this.co2Available) {
|
if (this.smartCostCo2) {
|
||||||
return this.fmtCo2Short(this.batteryGridChargeLimit);
|
return this.fmtCo2Short(this.batteryGridChargeLimit);
|
||||||
}
|
}
|
||||||
return this.fmtPricePerKWh(this.batteryGridChargeLimit, this.currency, true);
|
return this.fmtPricePerKWh(this.batteryGridChargeLimit, this.currency, true);
|
||||||
|
|
@ -532,27 +553,16 @@ export default defineComponent({
|
||||||
window.removeEventListener("resize", this.updateHeight);
|
window.removeEventListener("resize", this.updateHeight);
|
||||||
},
|
},
|
||||||
methods: {
|
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) {
|
detailsValue(price?: number, co2?: number) {
|
||||||
if (this.co2Available) {
|
return this.showCo2 ? co2 : price;
|
||||||
return co2;
|
|
||||||
}
|
|
||||||
return price;
|
|
||||||
},
|
},
|
||||||
detailsFmt(value: number) {
|
detailsFmt(value: number) {
|
||||||
if (this.co2Available) {
|
return this.showCo2
|
||||||
return this.fmtCo2Short(value);
|
? this.fmtCo2Short(value)
|
||||||
}
|
: this.fmtPricePerKWh(value, this.currency, true);
|
||||||
return this.fmtPricePerKWh(value, this.currency, true);
|
},
|
||||||
|
toggleCo2() {
|
||||||
|
settings.energyflowCo2 = !settings.energyflowCo2;
|
||||||
},
|
},
|
||||||
forecastFmt(value: number) {
|
forecastFmt(value: number) {
|
||||||
if (typeof value !== "number") return "";
|
if (typeof value !== "number") return "";
|
||||||
|
|
|
||||||
|
|
@ -30,7 +30,7 @@
|
||||||
<span class="text-end text-nowrap ps-1 fw-bold d-flex align-items-center">
|
<span class="text-end text-nowrap ps-1 fw-bold d-flex align-items-center">
|
||||||
<div
|
<div
|
||||||
ref="details"
|
ref="details"
|
||||||
class="fw-normal d-flex align-items-center"
|
class="fw-normal d-flex align-items-center user-select-none"
|
||||||
:class="{
|
:class="{
|
||||||
'text-decoration-underline': detailsClickable,
|
'text-decoration-underline': detailsClickable,
|
||||||
'evcc-gray': detailsInactive,
|
'evcc-gray': detailsInactive,
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@ const SETTINGS_UNIT = "settings_unit";
|
||||||
const SETTINGS_12H_FORMAT = "settings_12h_format";
|
const SETTINGS_12H_FORMAT = "settings_12h_format";
|
||||||
const SETTINGS_HIDDEN_FEATURES = "settings_hidden_features";
|
const SETTINGS_HIDDEN_FEATURES = "settings_hidden_features";
|
||||||
const SETTINGS_ENERGYFLOW_DETAILS = "settings_energyflow_details";
|
const SETTINGS_ENERGYFLOW_DETAILS = "settings_energyflow_details";
|
||||||
|
const SETTINGS_ENERGYFLOW_CO2 = "settings_energyflow_co2";
|
||||||
const SETTINGS_ENERGYFLOW_PV = "settings_energyflow_pv";
|
const SETTINGS_ENERGYFLOW_PV = "settings_energyflow_pv";
|
||||||
const SETTINGS_ENERGYFLOW_BATTERY = "settings_energyflow_battery";
|
const SETTINGS_ENERGYFLOW_BATTERY = "settings_energyflow_battery";
|
||||||
const SETTINGS_ENERGYFLOW_LOADPOINTS = "settings_energyflow_loadpoints";
|
const SETTINGS_ENERGYFLOW_LOADPOINTS = "settings_energyflow_loadpoints";
|
||||||
|
|
@ -105,6 +106,7 @@ export interface Settings {
|
||||||
is12hFormat: boolean;
|
is12hFormat: boolean;
|
||||||
hiddenFeatures: boolean;
|
hiddenFeatures: boolean;
|
||||||
energyflowDetails: boolean;
|
energyflowDetails: boolean;
|
||||||
|
energyflowCo2: boolean;
|
||||||
energyflowPv: boolean;
|
energyflowPv: boolean;
|
||||||
energyflowBattery: boolean;
|
energyflowBattery: boolean;
|
||||||
energyflowLoadpoints: boolean;
|
energyflowLoadpoints: boolean;
|
||||||
|
|
@ -126,6 +128,7 @@ const settings: Settings = reactive({
|
||||||
is12hFormat: readBool(SETTINGS_12H_FORMAT),
|
is12hFormat: readBool(SETTINGS_12H_FORMAT),
|
||||||
hiddenFeatures: readBool(SETTINGS_HIDDEN_FEATURES),
|
hiddenFeatures: readBool(SETTINGS_HIDDEN_FEATURES),
|
||||||
energyflowDetails: readBool(SETTINGS_ENERGYFLOW_DETAILS),
|
energyflowDetails: readBool(SETTINGS_ENERGYFLOW_DETAILS),
|
||||||
|
energyflowCo2: readBool(SETTINGS_ENERGYFLOW_CO2),
|
||||||
energyflowPv: readBool(SETTINGS_ENERGYFLOW_PV),
|
energyflowPv: readBool(SETTINGS_ENERGYFLOW_PV),
|
||||||
energyflowBattery: readBool(SETTINGS_ENERGYFLOW_BATTERY),
|
energyflowBattery: readBool(SETTINGS_ENERGYFLOW_BATTERY),
|
||||||
energyflowLoadpoints: readBool(SETTINGS_ENERGYFLOW_LOADPOINTS),
|
energyflowLoadpoints: readBool(SETTINGS_ENERGYFLOW_LOADPOINTS),
|
||||||
|
|
@ -146,6 +149,7 @@ watch(() => settings.unit, save(SETTINGS_UNIT));
|
||||||
watch(() => settings.is12hFormat, saveBool(SETTINGS_12H_FORMAT));
|
watch(() => settings.is12hFormat, saveBool(SETTINGS_12H_FORMAT));
|
||||||
watch(() => settings.hiddenFeatures, saveBool(SETTINGS_HIDDEN_FEATURES));
|
watch(() => settings.hiddenFeatures, saveBool(SETTINGS_HIDDEN_FEATURES));
|
||||||
watch(() => settings.energyflowDetails, saveBool(SETTINGS_ENERGYFLOW_DETAILS));
|
watch(() => settings.energyflowDetails, saveBool(SETTINGS_ENERGYFLOW_DETAILS));
|
||||||
|
watch(() => settings.energyflowCo2, saveBool(SETTINGS_ENERGYFLOW_CO2));
|
||||||
watch(() => settings.energyflowPv, saveBool(SETTINGS_ENERGYFLOW_PV));
|
watch(() => settings.energyflowPv, saveBool(SETTINGS_ENERGYFLOW_PV));
|
||||||
watch(() => settings.energyflowBattery, saveBool(SETTINGS_ENERGYFLOW_BATTERY));
|
watch(() => settings.energyflowBattery, saveBool(SETTINGS_ENERGYFLOW_BATTERY));
|
||||||
watch(() => settings.energyflowLoadpoints, saveBool(SETTINGS_ENERGYFLOW_LOADPOINTS));
|
watch(() => settings.energyflowLoadpoints, saveBool(SETTINGS_ENERGYFLOW_LOADPOINTS));
|
||||||
|
|
|
||||||
42
tests/energyflow.spec.ts
Normal file
42
tests/energyflow.spec.ts
Normal 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");
|
||||||
|
});
|
||||||
Loading…
Add table
Add a link
Reference in a new issue