From d697ba403f0df99aa27c7ed0e2ae88fb4e4c154c Mon Sep 17 00:00:00 2001 From: Thomas Speer Date: Wed, 15 Oct 2025 15:28:37 +0200 Subject: [PATCH 1/4] UI: optional battery state next to the engeryflow visualisation --- .../js/components/Energyflow/Energyflow.vue | 98 +++++++++++++++---- .../GlobalSettings/UserInterfaceSettings.vue | 33 +++++++ assets/js/settings.ts | 4 + i18n/de.json | 5 + i18n/en.json | 5 + 5 files changed, 124 insertions(+), 21 deletions(-) diff --git a/assets/js/components/Energyflow/Energyflow.vue b/assets/js/components/Energyflow/Energyflow.vue index aa90796cca..d84b2fb4cd 100644 --- a/assets/js/components/Energyflow/Energyflow.vue +++ b/assets/js/components/Energyflow/Energyflow.vue @@ -5,26 +5,43 @@ data-testid="energyflow" @click="toggleDetails" > -
- +
+
+ +
+
+
+ +
{{ batteryFmt(batterySoc) }}
+
+
, default: () => ({}) }, }, data: () => { - return { detailsOpen: false, detailsCompleteHeight: null as number | null, ready: false }; + return { + detailsOpen: false, + detailsCompleteHeight: null as number | null, + ready: false, + ICON_SIZE, + }; }, computed: { gridImport() { @@ -498,6 +523,9 @@ export default defineComponent({ count: this.activeLoadpointsCount, }); }, + showBatteryState() { + return this.batteryConfigured && settings.batteryState; + }, consumers() { return [...this.aux, ...this.ext]; }, @@ -659,4 +687,32 @@ export default defineComponent({ .legend-battery--mixed { clip-path: polygon(100% 0, 100% 100%, 0 100%); } +.battery-state-display { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + min-width: 55px; +} +@media (min-width: 768px) { + .battery-state-display { + min-width: 80px; + } +} +.battery-icon--charging { + color: var(--evcc-dark-green); +} +.battery-icon--discharging { + color: var(--bs-body-color); +} +.battery-icon--idle { + color: var(--bs-secondary); +} +.battery-soc-value { + font-size: 1rem; + font-weight: 600; + color: var(--bs-body-color); + width: 100%; + text-align: center; +} diff --git a/assets/js/components/GlobalSettings/UserInterfaceSettings.vue b/assets/js/components/GlobalSettings/UserInterfaceSettings.vue index f04a021f56..deec7ce784 100644 --- a/assets/js/components/GlobalSettings/UserInterfaceSettings.vue +++ b/assets/js/components/GlobalSettings/UserInterfaceSettings.vue @@ -90,6 +90,26 @@
+ +
+ +
+ +
+
+
{{ $t("settings.deviceInfo") }}
@@ -112,6 +132,8 @@ import { getHiddenFeatures, setHiddenFeatures } from "@/featureflags.ts"; import { isApp } from "@/utils/native"; import { defineComponent, type PropType } from "vue"; import { LENGTH_UNIT, THEME, type UiLoadpoint } from "@/types/evcc"; +import settings from "@/settings"; +import store from "@/store"; const TIME_12H = "12"; const TIME_24H = "24"; @@ -151,6 +173,17 @@ export default defineComponent({ window.matchMedia("(display-mode: standalone)").matches; return isSupported && !isPwa && !isApp(); }, + batteryConfigured() { + return (store.state.battery?.length || 0) > 0; + }, + batteryState: { + get() { + return settings.batteryState; + }, + set(value: boolean) { + settings.batteryState = value; + }, + }, }, watch: { unit(value) { diff --git a/assets/js/settings.ts b/assets/js/settings.ts index 6cba5e540f..c41ee1911c 100644 --- a/assets/js/settings.ts +++ b/assets/js/settings.ts @@ -12,6 +12,7 @@ const SETTINGS_ENERGYFLOW_PV = "settings_energyflow_pv"; const SETTINGS_ENERGYFLOW_BATTERY = "settings_energyflow_battery"; const SETTINGS_ENERGYFLOW_LOADPOINTS = "settings_energyflow_loadpoints"; const SETTINGS_ENERGYFLOW_CONSUMERS = "settings_energyflow_consumers"; +const SETTINGS_BATTERY_STATE = "settings_battery_state"; const LOADPOINTS = "loadpoints"; const SESSION_COLUMNS = "session_columns"; const SAVINGS_PERIOD = "savings_period"; @@ -96,6 +97,7 @@ export interface Settings { energyflowBattery: boolean; energyflowLoadpoints: boolean; energyflowConsumers: boolean; + batteryState: boolean; sessionColumns: string[]; savingsPeriod: string; savingsRegion: string; @@ -116,6 +118,7 @@ const settings: Settings = reactive({ energyflowBattery: readBool(SETTINGS_ENERGYFLOW_BATTERY), energyflowLoadpoints: readBool(SETTINGS_ENERGYFLOW_LOADPOINTS), energyflowConsumers: readBool(SETTINGS_ENERGYFLOW_CONSUMERS), + batteryState: readBool(SETTINGS_BATTERY_STATE), sessionColumns: readArray(SESSION_COLUMNS), savingsPeriod: read(SAVINGS_PERIOD), savingsRegion: read(SAVINGS_REGION), @@ -135,6 +138,7 @@ watch(() => settings.energyflowPv, saveBool(SETTINGS_ENERGYFLOW_PV)); watch(() => settings.energyflowBattery, saveBool(SETTINGS_ENERGYFLOW_BATTERY)); watch(() => settings.energyflowLoadpoints, saveBool(SETTINGS_ENERGYFLOW_LOADPOINTS)); watch(() => settings.energyflowConsumers, saveBool(SETTINGS_ENERGYFLOW_CONSUMERS)); +watch(() => settings.batteryState, saveBool(SETTINGS_BATTERY_STATE)); watch(() => settings.sessionColumns as string[], saveArray(SESSION_COLUMNS)); watch(() => settings.savingsPeriod, save(SAVINGS_PERIOD)); watch(() => settings.savingsRegion, save(SAVINGS_REGION)); diff --git a/i18n/de.json b/i18n/de.json index 4ecdd42e68..1544d21d30 100644 --- a/i18n/de.json +++ b/i18n/de.json @@ -1108,6 +1108,11 @@ "vehicle": "Fahrzeug" }, "settings": { + "batteryState": { + "label": "Batteriezustand", + "shortLabel": "Batterie", + "value": "Batteriezustand neben der Energiefluss-Visualisierung anzeigen." + }, "deviceInfo": "Einstellungen, die du in diesem Dialog vornimmst, gelten nur für dieses Gerät.", "fullscreen": { "enter": "Vollbild starten", diff --git a/i18n/en.json b/i18n/en.json index 0f3d733f3d..e796d436ba 100644 --- a/i18n/en.json +++ b/i18n/en.json @@ -1108,6 +1108,11 @@ "vehicle": "Vehicle" }, "settings": { + "batteryState": { + "label": "Battery State", + "shortLabel": "Battery", + "value": "Show battery state next to energy flow visualization." + }, "deviceInfo": "Settings you make in this dialog only affect this device.", "fullscreen": { "enter": "Enter fullscreen", From 046c2cfa9845cd4beaca9c7079d0aa4e3fe1c71c Mon Sep 17 00:00:00 2001 From: Thomas Speer Date: Wed, 15 Oct 2025 16:26:07 +0200 Subject: [PATCH 2/4] fix text in darkmode --- assets/js/components/Energyflow/Energyflow.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets/js/components/Energyflow/Energyflow.vue b/assets/js/components/Energyflow/Energyflow.vue index d84b2fb4cd..661d78352b 100644 --- a/assets/js/components/Energyflow/Energyflow.vue +++ b/assets/js/components/Energyflow/Energyflow.vue @@ -711,7 +711,7 @@ export default defineComponent({ .battery-soc-value { font-size: 1rem; font-weight: 600; - color: var(--bs-body-color); + color: var(--evcc-default-text); width: 100%; text-align: center; } From a8ffbe75a04c4e559bcd74226b21a6c6c18098e3 Mon Sep 17 00:00:00 2001 From: Thomas Speer Date: Wed, 15 Oct 2025 19:24:38 +0200 Subject: [PATCH 3/4] remove settings option put in the place of hold icon will show || when bat is on hold --- .../js/components/Energyflow/Energyflow.vue | 93 +++++++------------ .../GlobalSettings/UserInterfaceSettings.vue | 33 ------- assets/js/settings.ts | 4 - 3 files changed, 36 insertions(+), 94 deletions(-) diff --git a/assets/js/components/Energyflow/Energyflow.vue b/assets/js/components/Energyflow/Energyflow.vue index 661d78352b..e37aaaff49 100644 --- a/assets/js/components/Energyflow/Energyflow.vue +++ b/assets/js/components/Energyflow/Energyflow.vue @@ -5,42 +5,29 @@ data-testid="energyflow" @click="toggleDetails" > -
-
- -
+
+
-
- -
{{ batteryFmt(batterySoc) }}
-
+ +
{{ batterySocFmt(batterySoc) }}
this.fmtPercentage(soc, 0); }, + batterySocFmt() { + return (soc: number) => Math.round(soc).toString(); + }, fmtLoadpointSoc() { return (soc: number) => this.fmtPercentage(soc, 0); }, @@ -524,7 +514,7 @@ export default defineComponent({ }); }, showBatteryState() { - return this.batteryConfigured && settings.batteryState; + return this.batteryConfigured; }, consumers() { return [...this.aux, ...this.ext]; @@ -687,32 +677,21 @@ export default defineComponent({ .legend-battery--mixed { clip-path: polygon(100% 0, 100% 100%, 0 100%); } +.visualization-wrapper { + position: relative; +} .battery-state-display { + position: absolute; + top: 2.2rem; + right: -0.35rem; display: flex; flex-direction: column; align-items: center; - justify-content: center; - min-width: 55px; -} -@media (min-width: 768px) { - .battery-state-display { - min-width: 80px; - } -} -.battery-icon--charging { - color: var(--evcc-dark-green); -} -.battery-icon--discharging { - color: var(--bs-body-color); -} -.battery-icon--idle { - color: var(--bs-secondary); + color: var(--evcc-gray); } .battery-soc-value { - font-size: 1rem; - font-weight: 600; - color: var(--evcc-default-text); - width: 100%; - text-align: center; + font-size: 0.75rem; + font-weight: 400; + white-space: nowrap; } diff --git a/assets/js/components/GlobalSettings/UserInterfaceSettings.vue b/assets/js/components/GlobalSettings/UserInterfaceSettings.vue index deec7ce784..f04a021f56 100644 --- a/assets/js/components/GlobalSettings/UserInterfaceSettings.vue +++ b/assets/js/components/GlobalSettings/UserInterfaceSettings.vue @@ -90,26 +90,6 @@
- -
- -
- -
-
-
{{ $t("settings.deviceInfo") }}
@@ -132,8 +112,6 @@ import { getHiddenFeatures, setHiddenFeatures } from "@/featureflags.ts"; import { isApp } from "@/utils/native"; import { defineComponent, type PropType } from "vue"; import { LENGTH_UNIT, THEME, type UiLoadpoint } from "@/types/evcc"; -import settings from "@/settings"; -import store from "@/store"; const TIME_12H = "12"; const TIME_24H = "24"; @@ -173,17 +151,6 @@ export default defineComponent({ window.matchMedia("(display-mode: standalone)").matches; return isSupported && !isPwa && !isApp(); }, - batteryConfigured() { - return (store.state.battery?.length || 0) > 0; - }, - batteryState: { - get() { - return settings.batteryState; - }, - set(value: boolean) { - settings.batteryState = value; - }, - }, }, watch: { unit(value) { diff --git a/assets/js/settings.ts b/assets/js/settings.ts index c41ee1911c..6cba5e540f 100644 --- a/assets/js/settings.ts +++ b/assets/js/settings.ts @@ -12,7 +12,6 @@ const SETTINGS_ENERGYFLOW_PV = "settings_energyflow_pv"; const SETTINGS_ENERGYFLOW_BATTERY = "settings_energyflow_battery"; const SETTINGS_ENERGYFLOW_LOADPOINTS = "settings_energyflow_loadpoints"; const SETTINGS_ENERGYFLOW_CONSUMERS = "settings_energyflow_consumers"; -const SETTINGS_BATTERY_STATE = "settings_battery_state"; const LOADPOINTS = "loadpoints"; const SESSION_COLUMNS = "session_columns"; const SAVINGS_PERIOD = "savings_period"; @@ -97,7 +96,6 @@ export interface Settings { energyflowBattery: boolean; energyflowLoadpoints: boolean; energyflowConsumers: boolean; - batteryState: boolean; sessionColumns: string[]; savingsPeriod: string; savingsRegion: string; @@ -118,7 +116,6 @@ const settings: Settings = reactive({ energyflowBattery: readBool(SETTINGS_ENERGYFLOW_BATTERY), energyflowLoadpoints: readBool(SETTINGS_ENERGYFLOW_LOADPOINTS), energyflowConsumers: readBool(SETTINGS_ENERGYFLOW_CONSUMERS), - batteryState: readBool(SETTINGS_BATTERY_STATE), sessionColumns: readArray(SESSION_COLUMNS), savingsPeriod: read(SAVINGS_PERIOD), savingsRegion: read(SAVINGS_REGION), @@ -138,7 +135,6 @@ watch(() => settings.energyflowPv, saveBool(SETTINGS_ENERGYFLOW_PV)); watch(() => settings.energyflowBattery, saveBool(SETTINGS_ENERGYFLOW_BATTERY)); watch(() => settings.energyflowLoadpoints, saveBool(SETTINGS_ENERGYFLOW_LOADPOINTS)); watch(() => settings.energyflowConsumers, saveBool(SETTINGS_ENERGYFLOW_CONSUMERS)); -watch(() => settings.batteryState, saveBool(SETTINGS_BATTERY_STATE)); watch(() => settings.sessionColumns as string[], saveArray(SESSION_COLUMNS)); watch(() => settings.savingsPeriod, save(SAVINGS_PERIOD)); watch(() => settings.savingsRegion, save(SAVINGS_REGION)); From 7feb2941a326364cbf0e35a1ac643e6d0f49e14d Mon Sep 17 00:00:00 2001 From: Thomas Speer Date: Wed, 15 Oct 2025 19:33:41 +0200 Subject: [PATCH 4/4] number a little closer to the bat to provide better spacing --- assets/js/components/Energyflow/Energyflow.vue | 1 + 1 file changed, 1 insertion(+) diff --git a/assets/js/components/Energyflow/Energyflow.vue b/assets/js/components/Energyflow/Energyflow.vue index e37aaaff49..5ee0c5a6e9 100644 --- a/assets/js/components/Energyflow/Energyflow.vue +++ b/assets/js/components/Energyflow/Energyflow.vue @@ -692,6 +692,7 @@ export default defineComponent({ .battery-soc-value { font-size: 0.75rem; font-weight: 400; + line-height: 0.9; white-space: nowrap; }