Optimizer: don't show outdated battery forecast (#32119)

This commit is contained in:
andig 2026-07-24 15:24:39 +02:00 • committed by GitHub
parent f3a0241777
commit 43517ee105
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 28 additions and 15 deletions

View file

@ -1,5 +1,5 @@
<template>
<div class="d-flex flex-column gap-2">
<div v-if="suggestionValue || highest || lowest" class="d-flex flex-column gap-2">
<div v-if="suggestion && suggestionValue" class="d-flex justify-content-between gap-3">
<span class="d-flex align-items-center gap-2 fw-bold">
<Optimizer />
@ -12,23 +12,19 @@
<BatterySuggestionIcon :action="suggestion.action" />
</span>
</div>
<div v-if="forecast?.highest" class="d-flex justify-content-between gap-3">
<div v-if="highest" class="d-flex justify-content-between gap-3">
<span class="d-flex align-items-center gap-2 fw-bold">
<BatteryIcon :soc="forecast.highest.soc" />
{{ pointLabel(forecast.highest, true) }}
<BatteryIcon :soc="highest.soc" />
{{ pointLabel(highest, true) }}
</span>
<span class="text-muted text-end">{{
fmtDayTime(new Date(forecast.highest.time))
}}</span>
<span class="text-muted text-end">{{ fmtDayTime(new Date(highest.time)) }}</span>
</div>
<div v-if="forecast?.lowest" class="d-flex justify-content-between gap-3">
<div v-if="lowest" class="d-flex justify-content-between gap-3">
<span class="d-flex align-items-center gap-2 fw-bold">
<BatteryIcon :soc="forecast.lowest.soc" />
{{ pointLabel(forecast.lowest, false) }}
<BatteryIcon :soc="lowest.soc" />
{{ pointLabel(lowest, false) }}
</span>
<span class="text-muted text-end">{{
fmtDayTime(new Date(forecast.lowest.time))
}}</span>
<span class="text-muted text-end">{{ fmtDayTime(new Date(lowest.time)) }}</span>
</div>
</div>
</template>
@ -36,22 +32,37 @@
<script lang="ts">
import { defineComponent, type PropType } from "vue";
import formatter from "@/mixins/formatter";
import minuteTicker from "@/mixins/minuteTicker";
import type { BatteryForecast, BatteryForecastPoint, BatterySuggestion } from "@/types/evcc";
import BatteryIcon from "../Energyflow/BatteryIcon.vue";
import Optimizer from "../MaterialIcon/Optimizer.vue";
import BatterySuggestionIcon from "./BatterySuggestionIcon.vue";
// hide forecast points whose time has passed
function upcoming(
point: BatteryForecastPoint | null | undefined,
now: Date
): BatteryForecastPoint | null {
return point && new Date(point.time) > now ? point : null;
}
// Optimizer rows: suggestion plus the battery high/low soc forecast.
// Simple bold-label / muted-value layout.
export default defineComponent({
name: "OptimizerInfo",
components: { BatteryIcon, Optimizer, BatterySuggestionIcon },
mixins: [formatter],
mixins: [formatter, minuteTicker],
props: {
suggestion: { type: Object as PropType<BatterySuggestion | null>, default: null },
forecast: { type: Object as PropType<BatteryForecast | null>, default: null },
},
computed: {
highest(): BatteryForecastPoint | null {
return upcoming(this.forecast?.highest, this.everyMinute);
},
lowest(): BatteryForecastPoint | null {
return upcoming(this.forecast?.lowest, this.everyMinute);
},
suggestionValue(): string | null {
const action = this.suggestion?.action;
if (!action) return null;

View file

@ -271,9 +271,11 @@ func (site *Site) publishSuggestions() {
}
}
// clearSuggestions removes all suggestions when the optimizer result is stale
// clearSuggestions removes all suggestions and the battery forecast when the
// optimizer result is stale
func (site *Site) clearSuggestions() {
site.setSuggestions(nil, nil)
site.battery.Forecast = nil
site.publishBattery()
site.publishSuggestions()