Sessions: record added range per charging session (#32509)
This commit is contained in:
parent
addbda0c43
commit
3ec61c1851
12 changed files with 56 additions and 10 deletions
|
|
@ -121,6 +121,12 @@
|
|||
{{ socRange }}
|
||||
</td>
|
||||
</tr>
|
||||
<tr v-if="session.addedRange != null" data-testid="session-details-added-range">
|
||||
<th class="align-baseline">
|
||||
{{ $t("session.addedRange") }}
|
||||
</th>
|
||||
<td>+{{ formatKm(session.addedRange) }}</td>
|
||||
</tr>
|
||||
<tr data-testid="session-details-odometer">
|
||||
<th class="align-middle">
|
||||
{{ $t("session.odometer") }}
|
||||
|
|
|
|||
|
|
@ -260,6 +260,13 @@ export default defineComponent({
|
|||
value: (session) => session.chargeDuration,
|
||||
format: (value) => this.fmtDurationNs(value, false, "h"),
|
||||
},
|
||||
{
|
||||
name: "addedRange",
|
||||
unit: distanceUnit(),
|
||||
total: this.addedRange,
|
||||
value: (session) => session.addedRange || null,
|
||||
format: (value) => `+${this.fmtNumber(distanceValue(value), 0)}`,
|
||||
},
|
||||
{
|
||||
name: "odometer",
|
||||
unit: distanceUnit(),
|
||||
|
|
@ -363,6 +370,11 @@ export default defineComponent({
|
|||
chargeDuration() {
|
||||
return this.filteredSessions.reduce((total, s) => total + s.chargeDuration, 0);
|
||||
},
|
||||
addedRange() {
|
||||
const sessions = this.filteredSessions.filter((s) => s.addedRange != null);
|
||||
if (!sessions.length) return null;
|
||||
return sessions.reduce((total, s) => total + (s.addedRange || 0), 0);
|
||||
},
|
||||
price() {
|
||||
return this.filteredSessions.reduce((total, s) => total + (s.price || 0), 0);
|
||||
},
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ export interface Session {
|
|||
chargeDuration: number;
|
||||
socStart: number | null;
|
||||
socEnd: number | null;
|
||||
addedRange: number | null;
|
||||
solarPercentage: number;
|
||||
price: number | null;
|
||||
pricePerKWh: number | null;
|
||||
|
|
|
|||
|
|
@ -174,6 +174,7 @@ type Loadpoint struct {
|
|||
|
||||
// charge progress
|
||||
vehicleSoc float64 // Vehicle or charger soc
|
||||
vehicleRange int64 // Vehicle range in km
|
||||
chargeDuration time.Duration // Charge duration
|
||||
connectedDuration time.Duration // Connection duration
|
||||
energyMetrics EnergyMetrics // Stats for charged energy by session
|
||||
|
|
@ -1979,6 +1980,7 @@ func (lp *Loadpoint) publishSocAndRange() {
|
|||
if vs, ok := api.Cap[api.VehicleRange](lp.GetVehicle()); ok {
|
||||
if rng, err := vs.Range(); err == nil {
|
||||
lp.log.DEBUG.Printf("vehicle range: %dkm", rng)
|
||||
lp.vehicleRange = rng
|
||||
lp.publish(keys.VehicleRange, rng)
|
||||
} else if !loadpoint.AcceptableError(err) {
|
||||
lp.log.ERROR.Printf("vehicle range: %v", err)
|
||||
|
|
|
|||
|
|
@ -74,6 +74,12 @@ func (lp *Loadpoint) applyEnergyMetrics(s *session.Session) {
|
|||
s.SocEnd = &soc
|
||||
}
|
||||
|
||||
// added range by linear projection from current range and soc delta
|
||||
if rng := float64(lp.vehicleRange); rng > 0 && s.SocStart != nil && s.SocEnd != nil && *s.SocEnd > *s.SocStart {
|
||||
added := rng * (*s.SocEnd - *s.SocStart) / *s.SocEnd
|
||||
s.AddedRange = &added
|
||||
}
|
||||
|
||||
s.SolarPercentage = new(lp.energyMetrics.SolarPercentage())
|
||||
s.Price = lp.energyMetrics.Price()
|
||||
s.PricePerKWh = lp.energyMetrics.PricePerKWh()
|
||||
|
|
|
|||
|
|
@ -224,6 +224,7 @@ func (lp *Loadpoint) unpublishVehicleIdentity() {
|
|||
// unpublishVehicle resets published vehicle data
|
||||
func (lp *Loadpoint) unpublishVehicle() {
|
||||
lp.vehicleSoc = 0
|
||||
lp.vehicleRange = 0
|
||||
|
||||
lp.publish(keys.VehicleClimaterActive, nil)
|
||||
lp.publish(keys.VehicleSoc, 0.0)
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ type Session struct {
|
|||
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"`
|
||||
AddedRange *float64 `json:"addedRange" csv:"Added Range (km)" gorm:"column:added_range" 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"`
|
||||
|
|
|
|||
|
|
@ -1582,6 +1582,7 @@
|
|||
},
|
||||
"session": {
|
||||
"addOdometer": "Wert eintragen",
|
||||
"addedRange": "Geladene Reichweite",
|
||||
"cancel": "Abbrechen",
|
||||
"co2": "CO₂",
|
||||
"date": "Zeitraum",
|
||||
|
|
@ -1597,6 +1598,7 @@
|
|||
"title": "Ladevorgang"
|
||||
},
|
||||
"sessions": {
|
||||
"addedRange": "Reichweite",
|
||||
"avgPower": "⌀ Leistung",
|
||||
"avgPrice": "⌀ Preis",
|
||||
"chargeDuration": "Ladedauer",
|
||||
|
|
@ -1622,6 +1624,7 @@
|
|||
},
|
||||
"co2": "⌀ CO₂",
|
||||
"csv": {
|
||||
"addedrange": "Geladene Reichweite (km)",
|
||||
"chargedenergy": "Energie (kWh)",
|
||||
"chargeduration": "Ladedauer",
|
||||
"co2perkwh": "CO₂/kWh",
|
||||
|
|
|
|||
|
|
@ -1582,6 +1582,7 @@
|
|||
},
|
||||
"session": {
|
||||
"addOdometer": "Add value",
|
||||
"addedRange": "Added range",
|
||||
"cancel": "Cancel",
|
||||
"co2": "CO₂",
|
||||
"date": "Period",
|
||||
|
|
@ -1592,11 +1593,12 @@
|
|||
"meterstop": "Meter stop",
|
||||
"odometer": "Mileage",
|
||||
"price": "Cost",
|
||||
"soc": "Vehicle SoC",
|
||||
"soc": "Charge",
|
||||
"started": "Started",
|
||||
"title": "Charging Session"
|
||||
},
|
||||
"sessions": {
|
||||
"addedRange": "Range",
|
||||
"avgPower": "⌀ Power",
|
||||
"avgPrice": "⌀ Price",
|
||||
"chargeDuration": "Duration",
|
||||
|
|
@ -1622,6 +1624,7 @@
|
|||
},
|
||||
"co2": "⌀ CO₂",
|
||||
"csv": {
|
||||
"addedrange": "Added range (km)",
|
||||
"chargedenergy": "Energy (kWh)",
|
||||
"chargeduration": "Duration",
|
||||
"co2perkwh": "CO₂/kWh",
|
||||
|
|
|
|||
|
|
@ -56,10 +56,11 @@ test("soc range", async ({ page }) => {
|
|||
await page.goto("/");
|
||||
await expect(page.getByTestId("vehicle-status")).toContainText("Charging");
|
||||
|
||||
// update SoC to 80% and energy to 10 kWh
|
||||
// update SoC to 80%, range to 400 km and energy to 10 kWh
|
||||
await page.goto(simulatorUrl());
|
||||
await loadpoint.getByLabel("Energy").fill("10");
|
||||
await vehicle.getByLabel("SoC").fill("80");
|
||||
await vehicle.getByLabel("Range").fill("400");
|
||||
await simulatorApply(page);
|
||||
|
||||
await page.goto("/");
|
||||
|
|
@ -79,4 +80,7 @@ test("soc range", async ({ page }) => {
|
|||
const modal = page.getByTestId("session-details");
|
||||
await expectModalVisible(modal);
|
||||
await expect(modal.getByTestId("session-details-soc")).toContainText("20 – 80%");
|
||||
|
||||
// added range: 400 km at 80% soc -> 400 * (80 - 20) / 80 = 300 km
|
||||
await expect(modal.getByTestId("session-details-added-range")).toContainText("+300 km");
|
||||
});
|
||||
|
|
|
|||
|
|
@ -55,12 +55,16 @@ test.describe("basics", async () => {
|
|||
await expect(page.getByTestId("sessions-nodata")).toHaveCount(0);
|
||||
await expect(page.getByRole("table")).toBeVisible();
|
||||
await expect(page.getByTestId("sessions-head")).toHaveCount(1);
|
||||
await expect(page.getByTestId("sessions-head").locator("th")).toHaveCount(10);
|
||||
await expect(page.getByTestId("sessions-head").locator("th")).toHaveCount(11);
|
||||
|
||||
await expect(page.getByTestId("sessions-head-energy")).toContainText("ChargedkWh");
|
||||
await expect(page.getByTestId("sessions-foot-energy")).toBeVisible();
|
||||
await expect(page.getByTestId("sessions-foot-energy")).toHaveText("20.0");
|
||||
|
||||
await expect(page.getByTestId("sessions-head-addedRange")).toContainText("Rangekm");
|
||||
await expect(page.getByTestId("sessions-foot-addedRange")).toBeVisible();
|
||||
await expect(page.getByTestId("sessions-foot-addedRange")).toHaveText("+80");
|
||||
|
||||
await expect(page.getByTestId("sessions-head-solar")).toContainText("Solar%");
|
||||
await expect(page.getByTestId("sessions-foot-solar")).toBeVisible();
|
||||
await expect(page.getByTestId("sessions-foot-solar")).toHaveText("67.3");
|
||||
|
|
@ -251,9 +255,9 @@ test.describe("columns desktop", async () => {
|
|||
await page.setViewportSize({ width: 800, height: 800 });
|
||||
await page.goto("/#/sessions?year=2023&month=3");
|
||||
await expect(
|
||||
page.getByTestId("sessions-head-avgPrice").locator("option[value=odometer]")
|
||||
page.getByTestId("sessions-head-price").locator("option[value=odometer]")
|
||||
).toHaveCount(1);
|
||||
await page.getByTestId("sessions-head-avgPrice").getByRole("combobox").selectOption("odometer");
|
||||
await page.getByTestId("sessions-head-price").getByRole("combobox").selectOption("odometer");
|
||||
await expect(page.getByTestId("sessions-head-odometer")).toBeVisible();
|
||||
await expect(page.getByTestId("sessions-entry").nth(0)).toContainText("12,345");
|
||||
await expect(page.getByTestId("sessions-foot-odometer")).toHaveText("");
|
||||
|
|
@ -391,6 +395,7 @@ test.describe("session details", async () => {
|
|||
await expect(modal.getByTestId("session-details-solar")).toContainText("0.0% (0.0 kWh)");
|
||||
await expect(modal.getByTestId("session-details-price")).toContainText("2.50 € 50.0 ct/kWh");
|
||||
await expect(modal.getByTestId("session-details-co2")).toHaveCount(0);
|
||||
await expect(modal.getByTestId("session-details-added-range")).toContainText("+25 km");
|
||||
await expect(modal.getByTestId("session-details-odometer")).toContainText("Add value");
|
||||
await expect(modal.getByTestId("session-details-meter")).toHaveCount(0);
|
||||
await expect(modal.getByTestId("session-details-delete")).toContainText("Delete");
|
||||
|
|
@ -413,6 +418,7 @@ test.describe("session details", async () => {
|
|||
await expect(modal.getByTestId("session-details-price")).toContainText("2.00 € 20.0 ct/kWh");
|
||||
await expect(modal.getByTestId("session-details-co2")).toContainText("3 kg");
|
||||
await expect(modal.getByTestId("session-details-co2")).toContainText("300 g/kWh");
|
||||
await expect(modal.getByTestId("session-details-added-range")).toContainText("+60 km");
|
||||
await expect(modal.getByTestId("session-details-odometer")).toContainText("12,345 km");
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -15,11 +15,12 @@ CREATE TABLE `sessions` (
|
|||
`price_per_kwh` real,
|
||||
`co2_per_kwh` real,
|
||||
`charge_duration` integer,
|
||||
`added_range` real,
|
||||
PRIMARY KEY (`id`)
|
||||
);
|
||||
|
||||
INSERT INTO `sessions` VALUES (1,'2023-03-01 08:00:00.0+02:00','2023-05-02 12:00:00.0+02:00','Carport',NULL,'blauer e-Golf',NULL,NULL,10,12345,100,2,0.2,300,3600000000000);
|
||||
INSERT INTO `sessions` VALUES (2,'2023-05-02 08:00:00.0+02:00','2023-05-02 12:00:00.0+02:00','Carport',NULL,'blauer e-Golf',NULL,NULL,10,NULL,100,2,0.2,NULL,1800000000000);
|
||||
INSERT INTO `sessions` VALUES (3,'2023-05-02 08:00:00.0+02:00','2023-05-02 12:00:00.0+02:00','Carport',NULL,'blauer e-Golf',NULL,NULL,2.5,NULL,88.21,0.75,0.3,NULL,NULL);
|
||||
INSERT INTO `sessions` VALUES (4,'2023-05-03 16:00:00.0+02:00','2023-05-03 20:00:00.0+02:00','Carport',NULL,'weißes Model 3',NULL,NULL,2.5,NULL,50,0.25,0.1,NULL,NULL);
|
||||
INSERT INTO `sessions` VALUES (5,'2023-05-04 22:00:00.0+02:00','2023-05-05 06:00:00.0+02:00','Garage',NULL,'weißes Model 3',NULL,NULL,5,NULL,0,2.5,0.5,null,3600000000000);
|
||||
INSERT INTO `sessions` VALUES (1,'2023-03-01 08:00:00.0+02:00','2023-05-02 12:00:00.0+02:00','Carport',NULL,'blauer e-Golf',NULL,NULL,10,12345,100,2,0.2,300,3600000000000,60);
|
||||
INSERT INTO `sessions` VALUES (2,'2023-05-02 08:00:00.0+02:00','2023-05-02 12:00:00.0+02:00','Carport',NULL,'blauer e-Golf',NULL,NULL,10,NULL,100,2,0.2,NULL,1800000000000,55);
|
||||
INSERT INTO `sessions` VALUES (3,'2023-05-02 08:00:00.0+02:00','2023-05-02 12:00:00.0+02:00','Carport',NULL,'blauer e-Golf',NULL,NULL,2.5,NULL,88.21,0.75,0.3,NULL,NULL,NULL);
|
||||
INSERT INTO `sessions` VALUES (4,'2023-05-03 16:00:00.0+02:00','2023-05-03 20:00:00.0+02:00','Carport',NULL,'weißes Model 3',NULL,NULL,2.5,NULL,50,0.25,0.1,NULL,NULL,NULL);
|
||||
INSERT INTO `sessions` VALUES (5,'2023-05-04 22:00:00.0+02:00','2023-05-05 06:00:00.0+02:00','Garage',NULL,'weißes Model 3',NULL,NULL,5,NULL,0,2.5,0.5,null,3600000000000,25);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue