Session: store vehicle SoC at start and end (#30050)

This commit is contained in:
andig 2026-06-02 13:35:45 +02:00 • committed by GitHub
parent 2241f54ed7
commit 1933be48f4
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 58 additions and 0 deletions

View file

@ -113,6 +113,14 @@
{{ fmtCo2Medium(session.co2PerKWh) }}
</td>
</tr>
<tr v-if="socRange" data-testid="session-details-soc">
<th class="align-baseline">
{{ $t("session.soc") }}
</th>
<td>
{{ socRange }}
</td>
</tr>
<tr v-if="session.odometer" data-testid="session-details-odometer">
<th>
{{ $t("session.odometer") }}
@ -205,6 +213,14 @@ export default defineComponent({
solarEnergy() {
return this.chargedEnergy * (this.session.solarPercentage / 100);
},
socRange(): string {
const { socStart, socEnd } = this.session;
if (socStart == null || socEnd == null) {
return "";
}
const added = socEnd - socStart;
return `${this.fmtPercentage(added, 0, true)} (${this.fmtNumber(socStart, 0)} – ${this.fmtPercentage(socEnd, 0)})`;
},
vehicleOptions(): SelectOption<string>[] {
return this.vehicles.map((v) => ({
name: v.title,

View file

@ -215,6 +215,18 @@ export default defineComponent({
value: (session) => session.chargedEnergy,
format: (value) => this.fmtWh(value * 1e3, POWER_UNIT.KW, false),
},
{
name: "chargedSoc",
unit: "%",
total: this.chargedSoc,
value: (session) => {
if (session.socStart != null && session.socEnd != null) {
return Math.max(0, session.socEnd - session.socStart);
}
return null;
},
format: (value) => `+${Math.round(value)}`,
},
{
name: "solar",
unit: "%",
@ -340,6 +352,16 @@ export default defineComponent({
chargedEnergy() {
return this.filteredSessions.reduce((total, s) => total + s.chargedEnergy, 0);
},
chargedSoc() {
const sessions = this.filteredSessions.filter(
(s) => s.socStart != null && s.socEnd != null
);
if (!sessions.length) return null;
return sessions.reduce(
(total, s) => total + Math.max(0, (s.socEnd || 0) - (s.socStart || 0)),
0
);
},
chargeDuration() {
return this.filteredSessions.reduce((total, s) => total + s.chargeDuration, 0);
},

View file

@ -10,6 +10,8 @@ export interface Session {
meterStop: number;
chargedEnergy: number;
chargeDuration: number;
socStart: number | null;
socEnd: number | null;
solarPercentage: number;
price: number | null;
pricePerKWh: number | null;

View file

@ -493,6 +493,10 @@ func (lp *Loadpoint) evChargeStartHandler() {
if session.Created.IsZero() {
session.Created = lp.clock.Now()
}
// capture start soc once available (may not be present at session start)
if soc := lp.vehicleSoc; session.SocStart == nil && soc > 0 && !lp.chargerHasFeature(api.Heating) {
session.SocStart = &soc
}
})
}

View file

@ -70,6 +70,10 @@ func (lp *Loadpoint) applyEnergyMetrics(s *session.Session) {
s.MeterStop = &meterStop
}
if soc := lp.vehicleSoc; soc > 0 && !lp.chargerHasFeature(api.Heating) {
s.SocEnd = &soc
}
s.SolarPercentage = new(lp.energyMetrics.SolarPercentage())
s.Price = lp.energyMetrics.Price()
s.PricePerKWh = lp.energyMetrics.PricePerKWh()

View file

@ -22,6 +22,8 @@ type Session struct {
MeterStop *float64 `json:"meterStop" csv:"Meter Stop (kWh)" gorm:"column:meter_end_kwh"`
ChargedEnergy float64 `json:"chargedEnergy" csv:"Charged Energy (kWh)" gorm:"column:charged_kwh"`
ChargeDuration *time.Duration `json:"chargeDuration" csv:"Charge Duration" gorm:"column:charge_duration"`
SocStart *float64 `json:"socStart" csv:"SoC Start (%)" gorm:"column:soc_start" format:"int"`
SocEnd *float64 `json:"socEnd" csv:"SoC End (%)" gorm:"column:soc_end" format:"int"`
SolarPercentage *float64 `json:"solarPercentage" csv:"Solar (%)" gorm:"column:solar_percentage"`
Price *float64 `json:"price" csv:"Price" gorm:"column:price"`
PricePerKWh *float64 `json:"pricePerKWh" csv:"Price/kWh" gorm:"column:price_per_kwh"`

View file

@ -1413,6 +1413,7 @@
"meterstop": "Endzählerstand",
"odometer": "Kilometerstand",
"price": "Kosten",
"soc": "Ladestand",
"started": "Startzeit",
"title": "Ladevorgang"
},
@ -1420,6 +1421,7 @@
"avgPower": "⌀ Leistung",
"avgPrice": "⌀ Preis",
"chargeDuration": "Ladedauer",
"chargedSoc": "Ladung",
"chartTitle": {
"avgCo2ByGroup": "⌀ CO₂ {byGroup}",
"avgPriceByGroup": "⌀ Preis {byGroup}",
@ -1453,6 +1455,8 @@
"odometer": "Kilometerstand (km)",
"price": "Kosten",
"priceperkwh": "Preis/kWh",
"socend": "Ladestand Ende (%)",
"socstart": "Ladestand Start (%)",
"solarpercentage": "Sonne (%)",
"vehicle": "Fahrzeug"
},

View file

@ -1413,6 +1413,7 @@
"meterstop": "Meter stop",
"odometer": "Mileage",
"price": "Cost",
"soc": "Vehicle SoC",
"started": "Started",
"title": "Charging Session"
},
@ -1420,6 +1421,7 @@
"avgPower": "⌀ Power",
"avgPrice": "⌀ Price",
"chargeDuration": "Duration",
"chargedSoc": "Charge",
"chartTitle": {
"avgCo2ByGroup": "⌀ CO₂ {byGroup}",
"avgPriceByGroup": "⌀ Price {byGroup}",
@ -1453,6 +1455,8 @@
"odometer": "Mileage (km)",
"price": "Cost",
"priceperkwh": "Price/kWh",
"socend": "SoC end (%)",
"socstart": "SoC start (%)",
"solarpercentage": "Solar (%)",
"vehicle": "Vehicle"
},