diff --git a/packages/modules/web_themes/koala/source/src/components/BatteryCard.vue b/packages/modules/web_themes/koala/source/src/components/BatteryCard.vue index 202d5a9390..26a386bfe1 100644 --- a/packages/modules/web_themes/koala/source/src/components/BatteryCard.vue +++ b/packages/modules/web_themes/koala/source/src/components/BatteryCard.vue @@ -36,12 +36,11 @@ v-if="showSettings" class="row q-mt-md justify-between text-subtitle2" > -
Laden mit Überschuss:
-
+
Ladepriorität:
+
diff --git a/packages/modules/web_themes/koala/source/src/components/BatterySettingsDialog.vue b/packages/modules/web_themes/koala/source/src/components/BatterySettingsDialog.vue index 483b4783e0..9252ca681e 100644 --- a/packages/modules/web_themes/koala/source/src/components/BatterySettingsDialog.vue +++ b/packages/modules/web_themes/koala/source/src/components/BatterySettingsDialog.vue @@ -4,19 +4,36 @@ :maximized="isSmallScreen" :backdrop-filter="isSmallScreen ? '' : 'blur(4px)'" > - +
-
Speicher-Beachtung:
-
{{ name }}
+
+
Speicher-Beachtung:
+
{{ name }}
+
- +
-
Überschuss primär für:
+
Ladepriorität:
+
@@ -26,6 +43,7 @@ import { computed, ref } from 'vue'; import { Screen } from 'quasar'; import BatteryModeButtons from './BatteryModeButtons.vue'; +import RangeSliderStandard from './RangeSliderStandard.vue'; import { useMqttStore } from 'src/stores/mqtt-store'; const isOpen = ref(false); @@ -48,4 +66,22 @@ const name = computed(() => { defineExpose({ open: () => (isOpen.value = true), }); + +const batteryMode = computed(() => mqttStore.batteryMode().value); + +const batteryRange = computed({ + get: () => mqttStore.batteryChargePriorityRange, + set: (value) => { + mqttStore.batteryChargePriorityRange = value; + }, +}); + diff --git a/packages/modules/web_themes/koala/source/src/components/RangeSliderStandard.vue b/packages/modules/web_themes/koala/source/src/components/RangeSliderStandard.vue new file mode 100644 index 0000000000..1a1dde3d26 --- /dev/null +++ b/packages/modules/web_themes/koala/source/src/components/RangeSliderStandard.vue @@ -0,0 +1,72 @@ + + + diff --git a/packages/modules/web_themes/koala/source/src/components/SliderStandard.vue b/packages/modules/web_themes/koala/source/src/components/SliderStandard.vue index 94e418666b..7902d31f81 100644 --- a/packages/modules/web_themes/koala/source/src/components/SliderStandard.vue +++ b/packages/modules/web_themes/koala/source/src/components/SliderStandard.vue @@ -1,11 +1,9 @@ - - diff --git a/packages/modules/web_themes/koala/source/src/composables/useBatteryModes.ts b/packages/modules/web_themes/koala/source/src/composables/useBatteryModes.ts index 27591cbdf4..12d260b412 100644 --- a/packages/modules/web_themes/koala/source/src/composables/useBatteryModes.ts +++ b/packages/modules/web_themes/koala/source/src/composables/useBatteryModes.ts @@ -16,10 +16,10 @@ export const useBatteryModes = () => { }, { value: 'min_soc_bat_mode', - label: 'Mindest-SoC', + label: 'Nach SoC des Speichers', color: 'primary', icon: 'battery_4_bar', - tooltip: 'Mindest-SoC des Speichers', + tooltip: 'Nach SoC des Speichers', }, ]; diff --git a/packages/modules/web_themes/koala/source/src/composables/useDelayModel.ts b/packages/modules/web_themes/koala/source/src/composables/useDelayModel.ts new file mode 100644 index 0000000000..f3cfb3dec5 --- /dev/null +++ b/packages/modules/web_themes/koala/source/src/composables/useDelayModel.ts @@ -0,0 +1,80 @@ +import { ref, computed, watch, onBeforeUnmount } from 'vue' + +function clone(value: T): T { + if (typeof value === 'object' && value !== null) { + return { ...value } + } + return value +} + +type Comparable = number | Record +function isEqual(a: T, b: T): boolean { + if (typeof a === 'object' && a !== null) { + const objA = a as Record + const objB = b as Record + + return Object.keys(objA).every( + (key) => objA[key] === objB[key] + ) + } + return a === b +} + +export function useDelayModel( + props: { modelValue: T }, + emit: (event: 'update:model-value', value: T) => void, + delay = 2000 +) { + const tempValue = ref(clone(props.modelValue)) + + const updateTimeout = ref(null) + + const updatePending = computed(() => { + return !isEqual(tempValue.value, props.modelValue) + }) + + const delayedValue = computed({ + get: () => tempValue.value, + set: (newValue: T) => { + if (updateTimeout.value) { + clearTimeout(updateTimeout.value) + } + tempValue.value = clone(newValue) + }, + }) + + watch( + delayedValue, + (newValue) => { + if (!updatePending.value) return + + if (updateTimeout.value) { + clearTimeout(updateTimeout.value) + } + + updateTimeout.value = setTimeout(() => { + emit('update:model-value', clone(newValue)) + }, delay) + }, + { deep: true } + ) + + watch( + () => props.modelValue, + (newValue) => { + tempValue.value = clone(newValue) + } + ) + + onBeforeUnmount(() => { + if (updateTimeout.value) { + clearTimeout(updateTimeout.value) + emit('update:model-value', clone(tempValue.value)) + } + }) + + return { + delayedValue, + updatePending, + } +} diff --git a/packages/modules/web_themes/koala/source/src/stores/mqtt-store-model.ts b/packages/modules/web_themes/koala/source/src/stores/mqtt-store-model.ts index 9aaa806cd4..015d773507 100644 --- a/packages/modules/web_themes/koala/source/src/stores/mqtt-store-model.ts +++ b/packages/modules/web_themes/koala/source/src/stores/mqtt-store-model.ts @@ -224,3 +224,8 @@ export interface CounterConfiguration { id: number; configuration: object; } + +export interface RangeValue { + min: number; + max: number; +} 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 06468262fa..4418e66ed3 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 @@ -27,6 +27,7 @@ import type { VehicleChargeTarget, CalculatedSocState, SystemCommandEvent, + RangeValue, } from './mqtt-store-model'; export const useMqttStore = defineStore('mqtt', () => { @@ -1921,6 +1922,39 @@ export const useMqttStore = defineStore('mqtt', () => { }); }; + /** + * Get or set the battery charge priority SoC range for PV charging + * @returns RangeValue + */ + const batteryChargePriorityRange = computed({ + get() { + const minSoc = getValue.value( + 'openWB/general/chargemode_config/pv_charging/min_bat_soc', + ) as number | undefined; + const maxSoc = getValue.value( + 'openWB/general/chargemode_config/pv_charging/max_bat_soc', + ) as number | undefined; + return { + min: minSoc ?? 0, + max: maxSoc ?? 100, + }; + }, + set(newRange: RangeValue) { + updateTopic( + 'openWB/general/chargemode_config/pv_charging/min_bat_soc', + newRange.min, + undefined, + true, + ); + updateTopic( + 'openWB/general/chargemode_config/pv_charging/max_bat_soc', + newRange.max, + undefined, + true, + ); + }, + }); + /** * Get or set the charge point connected vehicle eco energy limit identified by the charge point id * @param chargePointId charge point id @@ -4055,6 +4089,7 @@ export const useMqttStore = defineStore('mqtt', () => { batteryDailyImportedTotal, batteryDailyExportedTotal, batteryTotalPower, + batteryChargePriorityRange, batteryMode, // Grid data getGridId,