diff --git a/assets/js/components/Sessions/SessionDetailsModal.vue b/assets/js/components/Sessions/SessionDetailsModal.vue
index 59b99efad..c44a373ed 100644
--- a/assets/js/components/Sessions/SessionDetailsModal.vue
+++ b/assets/js/components/Sessions/SessionDetailsModal.vue
@@ -113,6 +113,14 @@
{{ fmtCo2Medium(session.co2PerKWh) }}
+
+ |
+ {{ $t("session.soc") }}
+ |
+
+ {{ socRange }}
+ |
+
|
{{ $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[] {
return this.vehicles.map((v) => ({
name: v.title,
diff --git a/assets/js/components/Sessions/SessionTable.vue b/assets/js/components/Sessions/SessionTable.vue
index ce4a4fbe3..5c81e44f8 100644
--- a/assets/js/components/Sessions/SessionTable.vue
+++ b/assets/js/components/Sessions/SessionTable.vue
@@ -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);
},
diff --git a/assets/js/components/Sessions/types.ts b/assets/js/components/Sessions/types.ts
index c15755aaa..beb03b156 100644
--- a/assets/js/components/Sessions/types.ts
+++ b/assets/js/components/Sessions/types.ts
@@ -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;
diff --git a/core/loadpoint.go b/core/loadpoint.go
index 9ad9002dc..572304546 100644
--- a/core/loadpoint.go
+++ b/core/loadpoint.go
@@ -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
+ }
})
}
diff --git a/core/loadpoint_session.go b/core/loadpoint_session.go
index d30315ab8..843bdcde3 100644
--- a/core/loadpoint_session.go
+++ b/core/loadpoint_session.go
@@ -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()
diff --git a/core/session/session.go b/core/session/session.go
index 619c52477..82a538933 100644
--- a/core/session/session.go
+++ b/core/session/session.go
@@ -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"`
diff --git a/i18n/de.json b/i18n/de.json
index b8a8c4d2c..e0ebf2a22 100644
--- a/i18n/de.json
+++ b/i18n/de.json
@@ -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"
},
diff --git a/i18n/en.json b/i18n/en.json
index a1c853986..73f528edd 100644
--- a/i18n/en.json
+++ b/i18n/en.json
@@ -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"
},
|