diff --git a/packages/modules/web_themes/koala/source/public/icons/owbCounter.svg b/packages/modules/web_themes/koala/source/public/icons/owbCounter.svg new file mode 100644 index 0000000000..8a8f271fce --- /dev/null +++ b/packages/modules/web_themes/koala/source/public/icons/owbCounter.svg @@ -0,0 +1 @@ + diff --git a/packages/modules/web_themes/koala/source/src/components/ChartCarousel.vue b/packages/modules/web_themes/koala/source/src/components/ChartCarousel.vue index 17c95214e1..9925e73fc1 100644 --- a/packages/modules/web_themes/koala/source/src/components/ChartCarousel.vue +++ b/packages/modules/web_themes/koala/source/src/components/ChartCarousel.vue @@ -51,7 +51,7 @@ import { ref, computed, watch } from 'vue'; import { useQuasar } from 'quasar'; import EnergyFlowChart from './charts/energyFlowChart/EnergyFlowChart.vue'; import HistoryChart from './charts/historyChart/HistoryChart.vue'; -import DailyTotals from './DailyTotals.vue'; +import DailyTotals from './charts/dailyTotals/DailyTotals.vue'; import { useLocalDataStore } from 'src/stores/localData-store'; import { useMqttStore } from 'src/stores/mqtt-store'; diff --git a/packages/modules/web_themes/koala/source/src/components/DailyTotals.vue b/packages/modules/web_themes/koala/source/src/components/DailyTotals.vue deleted file mode 100644 index 508376bb91..0000000000 --- a/packages/modules/web_themes/koala/source/src/components/DailyTotals.vue +++ /dev/null @@ -1,443 +0,0 @@ - - - - - diff --git a/packages/modules/web_themes/koala/source/src/components/charts/dailyTotals/DailyTotals.vue b/packages/modules/web_themes/koala/source/src/components/charts/dailyTotals/DailyTotals.vue new file mode 100644 index 0000000000..7a9b8ae3e7 --- /dev/null +++ b/packages/modules/web_themes/koala/source/src/components/charts/dailyTotals/DailyTotals.vue @@ -0,0 +1,390 @@ + + + + + diff --git a/packages/modules/web_themes/koala/source/src/components/charts/dailyTotals/DailyTotalsRow.vue b/packages/modules/web_themes/koala/source/src/components/charts/dailyTotals/DailyTotalsRow.vue new file mode 100644 index 0000000000..a87bca1974 --- /dev/null +++ b/packages/modules/web_themes/koala/source/src/components/charts/dailyTotals/DailyTotalsRow.vue @@ -0,0 +1,264 @@ + + + + + diff --git a/packages/modules/web_themes/koala/source/src/components/charts/energyFlowChart/EnergyFlowChart.vue b/packages/modules/web_themes/koala/source/src/components/charts/energyFlowChart/EnergyFlowChart.vue index c3f0dc85f4..ea1200dd8a 100644 --- a/packages/modules/web_themes/koala/source/src/components/charts/energyFlowChart/EnergyFlowChart.vue +++ b/packages/modules/web_themes/koala/source/src/components/charts/energyFlowChart/EnergyFlowChart.vue @@ -49,7 +49,7 @@ const absoluteValueObject = (valueObject: ValueObject): ValueObject => { }; const gridPower = computed( - () => mqttStore.getGridPower('object') as ValueObject, + () => mqttStore.getCounterPower('object') as ValueObject, ); const gridConsumption = computed(() => Number(gridPower.value.value) > 0); const gridFeedIn = computed(() => Number(gridPower.value.value) < 0); @@ -286,18 +286,9 @@ const animationDurations = computed(() => { home: calcDuration(Number(homePower.value.value), maxPower), pv: calcDuration(Number(pvPower.value.value), maxPower), battery: calcDuration(Number(batteryPower.value.value), maxPower), - chargePoint1: calcDuration( - Number(chargePoint1Power.value.value), - maxPower, - ), - chargePoint2: calcDuration( - Number(chargePoint2Power.value.value), - maxPower, - ), - chargePoint3: calcDuration( - Number(chargePoint3Power.value.value), - maxPower, - ), + chargePoint1: calcDuration(Number(chargePoint1Power.value.value), maxPower), + chargePoint2: calcDuration(Number(chargePoint2Power.value.value), maxPower), + chargePoint3: calcDuration(Number(chargePoint3Power.value.value), maxPower), chargePointSum: calcDuration( Number(chargePointSumPower.value.value), maxPower, @@ -837,7 +828,7 @@ path.animatedReverse.grid { path.animated.home, path.animatedReverse.home { - stroke: var(--q-flow-home-stroke); + stroke: var(--q-home-stroke); animation-duration: v-bind('animationDurations.home'); } @@ -849,28 +840,28 @@ path.animatedReverse.pv { path.animated.battery, path.animatedReverse.battery { - stroke: var(--q-warning); + stroke: var(--q-battery-stroke); animation-duration: v-bind('animationDurations.battery'); } path.animated.charge-point-1, path.animatedReverse.charge-point-1 { - stroke: var(--q-primary); + stroke: var(--q-charge-point-stroke); animation-duration: v-bind('animationDurations.chargePoint1'); } path.animated.charge-point-2, path.animatedReverse.charge-point-2 { - stroke: var(--q-primary); + stroke: var(--q-charge-point-stroke); animation-duration: v-bind('animationDurations.chargePoint2'); } path.animated.charge-point-3, path.animatedReverse.charge-point-3 { - stroke: var(--q-primary); + stroke: var(--q-charge-point-stroke); animation-duration: v-bind('animationDurations.chargePoint3'); } path.animated.charge-point-sum, path.animatedReverse.charge-point-sum { - stroke: var(--q-primary); + stroke: var(--q-charge-point-stroke); animation-duration: v-bind('animationDurations.chargePointSum'); } @@ -909,7 +900,7 @@ path.animatedReverse.vehicle-3 { :root { path.home { - stroke: var(--q-grey); + stroke: var(--q-home-stroke); } } @@ -958,7 +949,7 @@ text .fill-success { } text .fill-danger { - fill: var(--q-negative); + fill: var(--q-grid-stroke); } text .fill-dark { @@ -966,42 +957,42 @@ text .fill-dark { } .grid text { - fill: var(--q-negative); + fill: var(--q-grid-stroke); } .grid circle, .grid rect { - stroke: var(--q-negative); + stroke: var(--q-grid-stroke); } .grid circle { - fill: color-mix(in srgb, var(--q-negative) 20%, transparent); + fill: var(--q-grid-fill); } .pv text { - fill: var(--q-positive); + fill: var(--q-pv-stroke); } .pv circle, .pv rect { - stroke: var(--q-positive); + stroke: var(--q-pv-stroke); } .pv circle { - fill: color-mix(in srgb, var(--q-positive) 30%, transparent); + fill: var(--q-pv-fill); } .battery text { - fill: var(--q-battery); + fill: var(--q-battery-stroke); } .battery circle, .battery rect { - stroke: var(--q-battery); + stroke: var(--q-battery-stroke); } .battery circle:not(.soc) { - fill: color-mix(in srgb, var(--q-battery) 50%, transparent); + fill: var(--q-battery-fill); } :root { @@ -1018,24 +1009,24 @@ text .fill-dark { .home circle, .home rect { - stroke: var(--q-flow-home-stroke); + stroke: var(--q-home-stroke); } .home circle { - fill: color-mix(in srgb, var(--q-brown-text) 20%, transparent); + fill: var(--q-home-fill); } .charge-point text { - fill: var(--q-primary); + fill: var(--q-charge-point-stroke); } .charge-point circle, .charge-point rect { - stroke: var(--q-primary); + stroke: var(--q-charge-point-stroke); } .charge-point circle { - fill: color-mix(in srgb, var(--q-primary) 30%, transparent); + fill: var(--q-charge-point-fill); } .background-circle { diff --git a/packages/modules/web_themes/koala/source/src/components/charts/historyChart/HistoryChart.vue b/packages/modules/web_themes/koala/source/src/components/charts/historyChart/HistoryChart.vue index 942141836d..8b8c1db139 100644 --- a/packages/modules/web_themes/koala/source/src/components/charts/historyChart/HistoryChart.vue +++ b/packages/modules/web_themes/koala/source/src/components/charts/historyChart/HistoryChart.vue @@ -118,13 +118,20 @@ const chartRange = computed( () => mqttStore.themeConfiguration?.history_chart_range || 3600, ); +const getGlobalColor = (name: string, fallback?: string) => { + const fromRoot = getComputedStyle(document.documentElement) + .getPropertyValue(name) + .trim(); + return fromRoot || fallback; +}; + const secondaryCounterDatasets = computed(() => mqttStore.getSecondaryCounterIds.map((id) => ({ label: mqttStore.getComponentName(id), category: 'component', unit: 'kW', - borderColor: '#FFA9A8', - backgroundColor: 'rgba(255,169,168, 0.2)', + borderColor: getGlobalColor('--q-secondary-counter-stroke'), + backgroundColor: getGlobalColor('--q-secondary-counter-fill'), data: selectedData.value.map((item) => ({ x: item.timestamp * 1000, y: item[`counter${id}-power`] ?? 0, @@ -143,8 +150,8 @@ const chargePointDatasets = computed(() => label: `${chargePointNames.value(cpId)}`, category: 'chargepoint', unit: 'kW', - borderColor: '#4766b5', - backgroundColor: 'rgba(71, 102, 181, 0.2)', + borderColor: getGlobalColor('--q-charge-point-stroke'), + backgroundColor: getGlobalColor('--q-charge-point-fill'), data: selectedData.value.map((item) => ({ x: item.timestamp * 1000, y: item[`cp${cpId}-power`] || 0, @@ -223,8 +230,8 @@ const lineChartData = computed(() => { label: gridMeterName.value, category: 'component', unit: 'kW', - borderColor: '#a33c42', - backgroundColor: 'rgba(239,182,188, 0.2)', + borderColor: getGlobalColor('--q-grid-stroke'), + backgroundColor: getGlobalColor('--q-grid-fill'), data: selectedData.value.map((item) => ({ x: item.timestamp * 1000, y: item.grid, @@ -240,8 +247,8 @@ const lineChartData = computed(() => { label: 'Hausverbrauch', category: 'component', unit: 'kW', - borderColor: '#949aa1', - backgroundColor: 'rgba(148, 154, 161, 0.2)', + borderColor: getGlobalColor('--q-home-stroke'), + backgroundColor: getGlobalColor('--q-home-fill'), data: selectedData.value.map((item) => ({ x: item.timestamp * 1000, y: item['house-power'], @@ -258,8 +265,8 @@ const lineChartData = computed(() => { label: 'PV ges.', category: 'component', unit: 'kW', - borderColor: 'green', - backgroundColor: 'rgba(144, 238, 144, 0.2)', + borderColor: getGlobalColor('--q-pv-stroke'), + backgroundColor: getGlobalColor('--q-pv-fill'), data: selectedData.value.map((item) => ({ x: item.timestamp * 1000, y: item['pv-all'], @@ -275,8 +282,8 @@ const lineChartData = computed(() => { label: 'Speicher ges.', category: 'component', unit: 'kW', - borderColor: '#b5a647', - backgroundColor: 'rgba(181, 166, 71, 0.2)', + borderColor: getGlobalColor('--q-battery-stroke'), + backgroundColor: getGlobalColor('--q-battery-fill'), data: selectedData.value.map((item) => ({ x: item.timestamp * 1000, y: item['bat-all-power'], diff --git a/packages/modules/web_themes/koala/source/src/css/quasar.variables.scss b/packages/modules/web_themes/koala/source/src/css/quasar.variables.scss index 286da62c15..0c0d3f7f87 100644 --- a/packages/modules/web_themes/koala/source/src/css/quasar.variables.scss +++ b/packages/modules/web_themes/koala/source/src/css/quasar.variables.scss @@ -35,7 +35,19 @@ $brown-text: #524f57; $white: #ffffff; $grey: #9e9e9e; // Quasar's default grey $toggle-off: #e0e0e0; +$grid-stroke: #a33c42; +$grid-fill: #efb6bc33; +$secondary-counter-stroke: #FFA9A8; +$secondary-counter-fill: #FFA9A833; +$home-stroke: #949aa1; +$home-fill: #949aa133; +$pv-stroke: #66bd7a; +$pv-fill: #90ee9033; $battery: #ba7128; +$battery-stroke: #ba7128; +$battery-fill: #ba712833; +$charge-point-stroke: #5c93d1; +$charge-point-fill: #5c93d14D; // Light theme (default) :root { --q-primary: #{$primary}; @@ -52,8 +64,19 @@ $battery: #ba7128; --q-grey: #{$grey}; --q-carousel-control: #{$primary}; --q-toggle-off: #{$toggle-off}; - --q-flow-home-stroke: #{$grey}; - --q-battery: #{$battery}; + --q-grid-fill: #{$grid-fill}; + --q-grid-stroke: #{$grid-stroke}; + --q-home-fill: #{$home-fill}; + --q-home-stroke: #{$home-stroke}; + --q-secondary-counter-stroke: #{$secondary-counter-stroke}; + --q-secondary-counter-fill: #{$secondary-counter-fill}; + --q-pv-stroke: #{$pv-stroke}; + --q-pv-fill: #{$pv-fill}; + --q-battery-stroke: #{$battery-stroke}; + --q-battery-fill: #{$battery-fill}; + --q-charge-point-stroke: #{$charge-point-stroke}; + --q-charge-point-fill: #{$charge-point-fill}; + // Main background background-color: var(--q-background-1); // Text color @@ -193,7 +216,7 @@ $battery: #ba7128; } // Scrollbar styling für .q-list auto Light Theme - .q-list { + .q-list, .narrow-scrollbar { scrollbar-width: thin; scrollbar-color: var(--q-primary) var(--q-background-2); } @@ -232,6 +255,14 @@ $dark-background-2: #010322; $dark-list: rgb(40, 42, 62); $dark-carousel: #8b8f9f; $dark-tab-icon: #d7d9e0; +$dark-daily-totals-grid-fill: #a13a41; +$dark-daily-totals-grid-stroke: #da959a; +$dark-daily-totals-battery-fill: #b97a1f; +$dark-daily-totals-battery-stroke: #e7c08a; +$dark-daily-totals-pv-fill: #27623a; +$dark-daily-totals-house-fill: #6e6e6e; +$dark-daily-totals-chargepoint-fill: #254a8c; + // Dark theme .body--dark { --q-primary: #{$dark-primary}; @@ -249,8 +280,26 @@ $dark-tab-icon: #d7d9e0; --q-toggle-off: #{$toggle-off}; --q-list: #{$dark-list}; --q-tab-icon: #{$dark-tab-icon}; - --q-flow-home-stroke: #{$grey}; - --q-battery: #{$dark-warning}; + --q-grid-fill: #{$grid-fill}; + --q-grid-stroke: #{$grid-stroke}; + --q-home-fill: #{$home-fill}; + --q-home-stroke: #{$home-stroke}; + --q-secondary-counter-stroke: #{$secondary-counter-stroke}; + --q-secondary-counter-fill: #{$secondary-counter-fill}; + --q-pv-stroke: #{$pv-stroke}; + --q-pv-fill: #{$pv-fill}; + --q-battery-stroke: #{$battery-stroke}; + --q-battery-fill: #{$battery-fill}; + --q-charge-point-stroke: #{$charge-point-stroke}; + --q-charge-point-fill: #{$charge-point-fill}; + --q-dark-daily-totals-grid-fill: #{$dark-daily-totals-grid-fill}; + --q-dark-daily-totals-grid-stroke: #{$dark-daily-totals-grid-stroke}; + --q-dark-daily-totals-battery-fill: #{$dark-daily-totals-battery-fill}; + --q-dark-daily-totals-battery-stroke: #{$dark-daily-totals-battery-stroke}; + --q-dark-daily-totals-pv-fill: #{$dark-daily-totals-pv-fill}; + --q-dark-daily-totals-house-fill: #{$dark-daily-totals-house-fill}; + --q-dark-daily-totals-chargepoint-fill: #{$dark-daily-totals-chargepoint-fill}; + // Main background background-color: $dark-page; // Text color diff --git a/packages/modules/web_themes/koala/source/src/stores/mqtt-store.ts b/packages/modules/web_themes/koala/source/src/stores/mqtt-store.ts index bad1ca5fcf..d3bde18597 100644 --- a/packages/modules/web_themes/koala/source/src/stores/mqtt-store.ts +++ b/packages/modules/web_themes/koala/source/src/stores/mqtt-store.ts @@ -3338,19 +3338,20 @@ export const useMqttStore = defineStore('mqtt', () => { }); /** - * Get grid power identified from root of component hierarchy + * Get counter power identified by root grid counter in component hierarchy or counterId * @param returnType type of return value, 'textValue', 'value', 'scaledValue', 'scaledUnit' or 'object' + * @param counterId counter ID * @returns string | number | ValueObject | undefined */ - const getGridPower = computed(() => { - return (returnType: string = 'textValue') => { - const gridId = getGridId.value; - if (gridId === undefined) { + const getCounterPower = computed(() => { + return (returnType: string = 'textValue', counterId?: number) => { + const id = counterId ?? getGridId.value; + if (id === undefined) { return '---'; } const power = (getValue.value( - `openWB/counter/${gridId}/get/power`, + `openWB/counter/${id}/get/power`, undefined, 0, ) as number) || 0; @@ -3366,19 +3367,20 @@ export const useMqttStore = defineStore('mqtt', () => { }); /** - * Get daily grid energy imported identified from root of component hierarchy + * Get daily counter energy imported identified by root grid counter in component hierarchy or counterId * @param returnType type of return value, 'textValue', 'value', 'scaledValue', 'scaledUnit' or 'object' + * @param counterId counter ID * @returns string | number | ValueObject | undefined */ - const gridDailyImported = computed(() => { - return (returnType: string = 'textValue') => { - const gridId = getGridId.value; - if (gridId === undefined) { + const counterDailyImported = computed(() => { + return (returnType: string = 'textValue', counterId?: number ) => { + const id = counterId ?? getGridId.value; + if (id === undefined) { return '---'; } const energy = (getValue.value( - `openWB/counter/${gridId}/get/daily_imported`, + `openWB/counter/${id}/get/daily_imported`, undefined, 0, ) as number) || 0; @@ -3394,19 +3396,20 @@ export const useMqttStore = defineStore('mqtt', () => { }); /** - * Get daily grid energy exported identified from root of component hierarchy + * Get daily counter energy exported identified by root grid counter in component hierarchy or counterId * @param returnType type of return value, 'textValue', 'value', 'scaledValue', 'scaledUnit' or 'object' + * @param counterId counter ID * @returns string | number | ValueObject | undefined */ - const gridDailyExported = computed(() => { - return (returnType: string = 'textValue') => { - const gridId = getGridId.value; - if (gridId === undefined) { + const counterDailyExported = computed(() => { + return (returnType: string = 'textValue', counterId?: number) => { + const id = counterId ?? getGridId.value; + if (id === undefined) { return '---'; } const energy = (getValue.value( - `openWB/counter/${gridId}/get/daily_exported`, + `openWB/counter/${id}/get/daily_exported`, undefined, 0, ) as number) || 0; @@ -3705,9 +3708,9 @@ export const useMqttStore = defineStore('mqtt', () => { getAllCounterIds, getSecondaryCounterIds, getComponentName, - getGridPower, - gridDailyImported, - gridDailyExported, + getCounterPower, + counterDailyImported, + counterDailyExported, // Home data getHomePower, homeDailyYield,