Count driving only and include charging losses
This commit is contained in:
@@ -1,5 +1,14 @@
|
||||
# Changelog
|
||||
|
||||
## 2.2.38
|
||||
|
||||
### Changed
|
||||
|
||||
- Charging cost now uses estimated grid energy: battery-side net kWh plus a fixed 10%
|
||||
charging loss. Both net and estimated grid energy are stored separately.
|
||||
- Driving consumption, duration, distance and SOC drop now accumulate only while the car
|
||||
is moving; parked HVAC and accessory use no longer inflate driving statistics.
|
||||
|
||||
## 2.2.37
|
||||
|
||||
### Added
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
3efa568d982f2594d596dca7d60bb84e39afd9b1f2db04c844f68c2e9af46ca2 FatihsMG4-unstable-2.2.38.apk
|
||||
+2
-2
@@ -34,8 +34,8 @@ android {
|
||||
applicationId "com.evsuite.abrp"
|
||||
minSdk 27
|
||||
targetSdk 36
|
||||
versionCode 54
|
||||
versionName "2.2.37"
|
||||
versionCode 55
|
||||
versionName "2.2.38"
|
||||
|
||||
// MongoDB credentials never ship in the APK. The app only talks to the small
|
||||
// authenticated API in server/, which owns the database connection.
|
||||
|
||||
@@ -24,6 +24,8 @@ import java.util.UUID;
|
||||
* reading the database needs to place it on a timeline — but nothing is measured from them.
|
||||
*/
|
||||
final class ChargeSessionTracker {
|
||||
static final double CHARGING_LOSS_PERCENT = 10d;
|
||||
static final double GRID_ENERGY_FACTOR = 1d + CHARGING_LOSS_PERCENT / 100d;
|
||||
private static final String PREFS = "charging_sessions_v1";
|
||||
private static final String ACTIVE = "active";
|
||||
private static final String LAST = "last_completed";
|
||||
@@ -67,6 +69,7 @@ final class ChargeSessionTracker {
|
||||
final Float startSocPercent;
|
||||
final Float currentSocPercent;
|
||||
final double energyKwh;
|
||||
final double gridEnergyKwh;
|
||||
final double pricePerKwh;
|
||||
final double totalCost;
|
||||
final List<Point> points;
|
||||
@@ -80,8 +83,9 @@ final class ChargeSessionTracker {
|
||||
this.startSocPercent = startSocPercent;
|
||||
this.currentSocPercent = currentSocPercent;
|
||||
this.energyKwh = energyKwh;
|
||||
this.gridEnergyKwh = gridEnergyKwh(energyKwh);
|
||||
this.pricePerKwh = pricePerKwh;
|
||||
this.totalCost = energyKwh * pricePerKwh;
|
||||
this.totalCost = totalCost(energyKwh, pricePerKwh);
|
||||
this.points = points;
|
||||
}
|
||||
}
|
||||
@@ -140,6 +144,14 @@ final class ChargeSessionTracker {
|
||||
return Double.longBitsToDouble(prefs.getLong(PRICE, 0L));
|
||||
}
|
||||
|
||||
static double gridEnergyKwh(double batteryEnergyKwh) {
|
||||
return batteryEnergyKwh * GRID_ENERGY_FACTOR;
|
||||
}
|
||||
|
||||
static double totalCost(double batteryEnergyKwh, double pricePerKwh) {
|
||||
return gridEnergyKwh(batteryEnergyKwh) * pricePerKwh;
|
||||
}
|
||||
|
||||
synchronized List<JSONObject> pendingUploads() {
|
||||
JSONArray array = pendingArray();
|
||||
List<JSONObject> result = new ArrayList<>();
|
||||
@@ -264,8 +276,12 @@ final class ChargeSessionTracker {
|
||||
}
|
||||
JSONObject json = new JSONObject().put("sessionId", id).put("startedAt", startedAtMs)
|
||||
.put("endedAt", endedAtMs).put("durationSeconds", activeSeconds())
|
||||
.put("energyKwh", energyKwh).put("pricePerKwh", pricePerKwh)
|
||||
.put("totalCost", energyKwh * pricePerKwh).put("curve", curve)
|
||||
.put("energyKwh", energyKwh)
|
||||
.put("chargingLossPercent", CHARGING_LOSS_PERCENT)
|
||||
.put("gridEnergyKwh", gridEnergyKwh(energyKwh))
|
||||
.put("pricePerKwh", pricePerKwh)
|
||||
.put("totalCost", totalCost(energyKwh, pricePerKwh))
|
||||
.put("curve", curve)
|
||||
.put("meteredSeconds", meteredSeconds)
|
||||
.put("curveIntervalMs", curveIntervalMs);
|
||||
if (startSoc != null) json.put("startSocPercent", startSoc);
|
||||
|
||||
@@ -10,7 +10,9 @@ package com.evsuite.abrp;
|
||||
* or a regeneration-discarding gross fallback. Three sources, three answers, none of them
|
||||
* agreeing with the car's dashboard — the counters were not measuring the same quantity.
|
||||
*
|
||||
* What the vehicle reports is net DC battery power: positive out of the pack, negative in.
|
||||
* Only moving intervals belong to these driving counters. Parked HVAC/accessory use is
|
||||
* deliberately excluded from distance, energy, time and SOC-drop totals. What the vehicle
|
||||
* reports while moving is net DC battery power: positive out of the pack, negative in.
|
||||
* Integrating it as-is means regeneration subtracts, which is exactly what the car's own
|
||||
* kWh figure does. Discarding the negative half (the old {@code max(0, kW)} fallback) is
|
||||
* what made our number read high.
|
||||
@@ -69,25 +71,20 @@ final class ConsumptionMath {
|
||||
: (previousSpeedKmh != null ? Math.abs(previousSpeedKmh) : 0f);
|
||||
float previous = previousSpeedKmh != null ? Math.abs(previousSpeedKmh) : current;
|
||||
float effectiveKmh = (previous + current) * 0.5f;
|
||||
if (effectiveKmh < SPEED_DEAD_BAND_KMH) effectiveKmh = 0f;
|
||||
else if (effectiveKmh > 90f) effectiveKmh *= 1.0035f;
|
||||
if (effectiveKmh < SPEED_DEAD_BAND_KMH) {
|
||||
return new Step(0d, 0d, 0d, 0d);
|
||||
} else if (effectiveKmh > 90f) effectiveKmh *= 1.0035f;
|
||||
else if (effectiveKmh > 30f) effectiveKmh *= 1.0015f;
|
||||
double km = effectiveKmh * hours;
|
||||
|
||||
// Energy: trapezoidal net DC power, regeneration included with its own sign.
|
||||
double kwh = 0d;
|
||||
if (powerKw != null && Math.abs(powerKw) <= MAX_PLAUSIBLE_KW) {
|
||||
if (current == 0f && powerKw < 0f) {
|
||||
// Standing still with power flowing in is charging or pre-conditioning
|
||||
// recovery, not a drive crediting itself energy it never spent.
|
||||
kwh = 0d;
|
||||
} else {
|
||||
double effectiveKw = previousPowerKw != null && Math.abs(previousPowerKw) <= MAX_PLAUSIBLE_KW
|
||||
? (previousPowerKw + powerKw) * 0.5d
|
||||
: powerKw;
|
||||
kwh = effectiveKw * hours;
|
||||
}
|
||||
}
|
||||
|
||||
double socDrop = (!charging && previousSocPercent != null && socPercent != null)
|
||||
? previousSocPercent - socPercent
|
||||
|
||||
@@ -119,7 +119,7 @@ public class MainActivity extends AppCompatActivity {
|
||||
private TextInputEditText consumptionSohInput;
|
||||
private java.time.LocalDate selectedConsumptionDate = java.time.LocalDate.now();
|
||||
private TextView chargeStatus, chargeSoc, chargeStartSoc, chargeVoltage, chargeCurrent, chargePower,
|
||||
chargeEnergy, chargeDuration, chargeTotalCost, chargeCurveTable;
|
||||
chargeEnergy, chargeGridEnergy, chargeDuration, chargeTotalCost, chargeCurveTable;
|
||||
private TextInputLayout chargePriceLayout;
|
||||
private TextInputEditText chargePriceInput;
|
||||
private ChargeSessionTracker chargeSessionTracker;
|
||||
@@ -936,6 +936,7 @@ public class MainActivity extends AppCompatActivity {
|
||||
chargeCurrent = chargingPane.findViewById(R.id.charge_current);
|
||||
chargePower = chargingPane.findViewById(R.id.charge_power);
|
||||
chargeEnergy = chargingPane.findViewById(R.id.charge_energy);
|
||||
chargeGridEnergy = chargingPane.findViewById(R.id.charge_grid_energy);
|
||||
chargeDuration = chargingPane.findViewById(R.id.charge_duration);
|
||||
chargeTotalCost = chargingPane.findViewById(R.id.charge_total_cost);
|
||||
chargeGraph = chargingPane.findViewById(R.id.charge_graph);
|
||||
@@ -980,6 +981,8 @@ public class MainActivity extends AppCompatActivity {
|
||||
chargeCurrent.setText(fmt(s.getBatteryCurrentA(), "%.1f A"));
|
||||
chargePower.setText(s.getBatteryPowerKw() == null ? "—" : String.format(java.util.Locale.getDefault(), "%.1f kW", Math.abs(s.getBatteryPowerKw())));
|
||||
chargeEnergy.setText(session.startedAtMs > 0 ? String.format(java.util.Locale.getDefault(), "%.2f kWh", session.energyKwh) : "—");
|
||||
chargeGridEnergy.setText(session.startedAtMs > 0 ? String.format(
|
||||
java.util.Locale.getDefault(), "%.2f kWh", session.gridEnergyKwh) : "—");
|
||||
long sec = session.durationSeconds;
|
||||
chargeDuration.setText(session.startedAtMs > 0 ? String.format(java.util.Locale.getDefault(), "%02d:%02d:%02d", sec/3600,(sec%3600)/60,sec%60) : "—");
|
||||
chargeTotalCost.setText(session.startedAtMs > 0 ? String.format(java.util.Locale.getDefault(), "%.2f ₺", session.totalCost) : "—");
|
||||
|
||||
@@ -9,7 +9,8 @@
|
||||
<TextView android:layout_width="0dp" android:layout_columnWeight="1" android:padding="10dp" android:text="Batarya voltajı" android:textAppearance="@style/DataLabel"/><TextView android:id="@+id/charge_voltage" android:layout_width="0dp" android:layout_columnWeight="1" android:padding="10dp" android:text="—" android:textAppearance="@style/DataValue"/>
|
||||
<TextView android:layout_width="0dp" android:layout_columnWeight="1" android:padding="10dp" android:text="Batarya akımı" android:textAppearance="@style/DataLabel"/><TextView android:id="@+id/charge_current" android:layout_width="0dp" android:layout_columnWeight="1" android:padding="10dp" android:text="—" android:textAppearance="@style/DataValue"/>
|
||||
<TextView android:layout_width="0dp" android:layout_columnWeight="1" android:padding="10dp" android:text="Şarj gücü" android:textAppearance="@style/DataLabel"/><TextView android:id="@+id/charge_power" android:layout_width="0dp" android:layout_columnWeight="1" android:padding="10dp" android:text="—" android:textAppearance="@style/DataValue"/>
|
||||
<TextView android:layout_width="0dp" android:layout_columnWeight="1" android:padding="10dp" android:text="Alınan enerji" android:textAppearance="@style/DataLabel"/><TextView android:id="@+id/charge_energy" android:layout_width="0dp" android:layout_columnWeight="1" android:padding="10dp" android:text="—" android:textAppearance="@style/DataValue"/>
|
||||
<TextView android:layout_width="0dp" android:layout_columnWeight="1" android:padding="10dp" android:text="Bataryaya giren net enerji" android:textAppearance="@style/DataLabel"/><TextView android:id="@+id/charge_energy" android:layout_width="0dp" android:layout_columnWeight="1" android:padding="10dp" android:text="—" android:textAppearance="@style/DataValue"/>
|
||||
<TextView android:layout_width="0dp" android:layout_columnWeight="1" android:padding="10dp" android:text="Şebekeden alınan (+%10)" android:textAppearance="@style/DataLabel"/><TextView android:id="@+id/charge_grid_energy" android:layout_width="0dp" android:layout_columnWeight="1" android:padding="10dp" android:text="—" android:textAppearance="@style/DataValue"/>
|
||||
<TextView android:layout_width="0dp" android:layout_columnWeight="1" android:padding="10dp" android:text="Şarj süresi" android:textAppearance="@style/DataLabel"/><TextView android:id="@+id/charge_duration" android:layout_width="0dp" android:layout_columnWeight="1" android:padding="10dp" android:text="—" android:textAppearance="@style/DataValue"/>
|
||||
<TextView android:layout_width="0dp" android:layout_columnWeight="1" android:padding="10dp" android:text="Toplam ücret" android:textAppearance="@style/DataLabel"/><TextView android:id="@+id/charge_total_cost" android:layout_width="0dp" android:layout_columnWeight="1" android:padding="10dp" android:text="—" android:textAppearance="@style/DataValue"/>
|
||||
</GridLayout>
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
package com.evsuite.abrp;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
public class ChargeCostTest {
|
||||
@Test
|
||||
public void costIncludesTenPercentChargingLoss() {
|
||||
assertEquals(55d, ChargeSessionTracker.gridEnergyKwh(50d), 0.000001d);
|
||||
assertEquals(550d, ChargeSessionTracker.totalCost(50d, 10d), 0.000001d);
|
||||
}
|
||||
}
|
||||
@@ -32,10 +32,13 @@ public class ConsumptionMathTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void speedBelowTheDeadBandContributesNoDistance() {
|
||||
public void speedBelowTheDeadBandContributesNothingToDrivingTotals() {
|
||||
ConsumptionMath.Step step =
|
||||
ConsumptionMath.integrate(DT, 2f, 2f, null, null, null, null, false);
|
||||
ConsumptionMath.integrate(DT, 2f, 2f, 8f, 8f, 80f, 79f, false);
|
||||
assertEquals(0d, step.km, TOLERANCE);
|
||||
assertEquals(0d, step.kwh, TOLERANCE);
|
||||
assertEquals(0d, step.hours, TOLERANCE);
|
||||
assertEquals(0d, step.socDrop, TOLERANCE);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -70,11 +73,11 @@ public class ConsumptionMathTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void standstillPowerFlowingOutStillCounts() {
|
||||
// Parked with the climate running is real consumption.
|
||||
public void parkedClimateUseIsExcludedFromDrivingConsumption() {
|
||||
ConsumptionMath.Step step =
|
||||
ConsumptionMath.integrate(DT, 0f, 0f, 2f, 2f, null, null, false);
|
||||
assertEquals(2d * DT, step.kwh, TOLERANCE);
|
||||
assertEquals(0d, step.kwh, TOLERANCE);
|
||||
assertEquals(0d, step.hours, TOLERANCE);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
+5
-5
@@ -1,5 +1,5 @@
|
||||
MONGODB_URI mongodb+srv://boklukunefe:9rprDcfHTjBYza7z@cluster0.1aarkvo.mongodb.net/?retryWrites=true&w=majority&appName=Cluster0
|
||||
MONGODB_DATABASE fatihsmg4
|
||||
INGEST_TOKEN 066da6fc58a0fcd8a35deccb29e43ef7a0b3bcda5a3c2b99db86e64a5b982ec6
|
||||
SESSION_SECRET a89b011dfe7bec22c4f44f89e8e0ef647e6ff3289e84ca1b5b68c413ff7f4b77
|
||||
DASHBOARD_PASSWORD <sen belirle, en az 8 karakter>
|
||||
MONGODB_URI=mongodb+srv://username:password@cluster.example.mongodb.net/
|
||||
MONGODB_DATABASE=fatihsmg4
|
||||
INGEST_TOKEN=replace-with-a-long-random-write-token
|
||||
SESSION_SECRET=replace-with-a-long-random-session-secret
|
||||
DASHBOARD_PASSWORD=replace-with-a-strong-dashboard-password
|
||||
|
||||
@@ -27,11 +27,12 @@ export default async function ChargingPage() {
|
||||
return (
|
||||
<Shell active="/charging" title="Şarj" subtitle={`${sessions.length} DC seans`}>
|
||||
<div className="tiles">
|
||||
<Tile label="Toplam alınan" value={precise(total.energyKwh)} unit="kWh" tone="gold" />
|
||||
<Tile label="Bataryaya giren net" value={precise(total.energyKwh)} unit="kWh" tone="gold" />
|
||||
<Tile label="Şebekeden alınan (+%10)" value={precise(total.gridEnergyKwh)} unit="kWh" tone="gold" />
|
||||
<Tile label="Toplam ödenen" value={money(total.cost)} tone="gold" />
|
||||
<Tile
|
||||
label="Ortalama birim"
|
||||
value={total.energyKwh > 0 ? money(total.cost / total.energyKwh) : "—"}
|
||||
value={total.gridEnergyKwh > 0 ? money(total.cost / total.gridEnergyKwh) : "—"}
|
||||
note="kWh başına"
|
||||
tone="teal"
|
||||
/>
|
||||
@@ -78,7 +79,8 @@ export default async function ChargingPage() {
|
||||
<Row label="Başlangıç" value={dateTime(session.startedAt)} />
|
||||
<Row label="Bitiş" value={dateTime(session.endedAt)} />
|
||||
<Row label="Ölçülen süre" value={duration(session.durationSeconds)} />
|
||||
<Row label="Alınan enerji" value={`${precise(session.energyKwh)} kWh`} />
|
||||
<Row label="Bataryaya giren net" value={`${precise(session.energyKwh)} kWh`} />
|
||||
<Row label="Şebekeden alınan (+%10)" value={`${precise(session.gridEnergyKwh ?? session.energyKwh * 1.1)} kWh`} />
|
||||
<Row label="Kazanılan doluluk" value={gained === null ? "—" : `%${num(gained)}`} />
|
||||
<Row label="Tepe güç" value={peak > 0 ? `${num(peak)} kW` : "—"} />
|
||||
<Row label="Ortalama güç" value={average > 0 ? `${num(average)} kW` : "—"} />
|
||||
|
||||
@@ -36,8 +36,8 @@ export default async function OverviewPage() {
|
||||
>
|
||||
<div className="tiles">
|
||||
<Tile
|
||||
label="Toplam alınan"
|
||||
value={precise(charge.energyKwh)}
|
||||
label="Şebekeden alınan"
|
||||
value={precise(charge.gridEnergyKwh)}
|
||||
unit="kWh"
|
||||
note={`${charge.sessions} DC şarj`}
|
||||
tone="gold"
|
||||
@@ -45,7 +45,7 @@ export default async function OverviewPage() {
|
||||
<Tile
|
||||
label="Ödenen"
|
||||
value={money(charge.cost)}
|
||||
note={charge.energyKwh > 0 ? `${money(charge.cost / charge.energyKwh)}/kWh ort.` : undefined}
|
||||
note={charge.gridEnergyKwh > 0 ? `${money(charge.cost / charge.gridEnergyKwh)}/kWh ort.` : undefined}
|
||||
tone="gold"
|
||||
/>
|
||||
<Tile label="Son 30 gün" value={num(recent.km)} unit="km" note={efficiency(recent.kwh, recent.km)} tone="teal" />
|
||||
@@ -68,7 +68,8 @@ export default async function OverviewPage() {
|
||||
<h2 className="card-title">Son şarj</h2>
|
||||
<div className="rows">
|
||||
<Row label="Tarih" value={dateTime(lastSession.startedAt)} />
|
||||
<Row label="Alınan enerji" value={`${precise(lastSession.energyKwh)} kWh`} />
|
||||
<Row label="Bataryaya giren net" value={`${precise(lastSession.energyKwh)} kWh`} />
|
||||
<Row label="Şebekeden alınan (+%10)" value={`${precise(lastSession.gridEnergyKwh ?? lastSession.energyKwh * 1.1)} kWh`} />
|
||||
<Row
|
||||
label="Doluluk"
|
||||
value={
|
||||
|
||||
+3
-1
@@ -47,6 +47,7 @@ export async function loadOverview(dayLimit = 60, sessionLimit = 60): Promise<Ov
|
||||
|
||||
export interface ChargeTotals {
|
||||
energyKwh: number;
|
||||
gridEnergyKwh: number;
|
||||
cost: number;
|
||||
sessions: number;
|
||||
seconds: number;
|
||||
@@ -58,6 +59,7 @@ export function sumCharging(sessions: ChargingSession[]): ChargeTotals {
|
||||
return sessions.reduce<ChargeTotals>(
|
||||
(total, session) => ({
|
||||
energyKwh: total.energyKwh + session.energyKwh,
|
||||
gridEnergyKwh: total.gridEnergyKwh + (session.gridEnergyKwh ?? session.energyKwh * 1.1),
|
||||
cost: total.cost + session.totalCost,
|
||||
sessions: total.sessions + 1,
|
||||
seconds: total.seconds + session.durationSeconds,
|
||||
@@ -68,7 +70,7 @@ export function sumCharging(sessions: ChargingSession[]): ChargeTotals {
|
||||
: 0),
|
||||
peakKw: Math.max(total.peakKw, ...session.curve.map((point) => point.powerKw), 0),
|
||||
}),
|
||||
{ energyKwh: 0, cost: 0, sessions: 0, seconds: 0, socGained: 0, peakKw: 0 },
|
||||
{ energyKwh: 0, gridEnergyKwh: 0, cost: 0, sessions: 0, seconds: 0, socGained: 0, peakKw: 0 },
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -41,6 +41,8 @@ export interface ChargingSession {
|
||||
endedAt: Date;
|
||||
durationSeconds: number;
|
||||
energyKwh: number;
|
||||
chargingLossPercent: number;
|
||||
gridEnergyKwh: number;
|
||||
pricePerKwh: number;
|
||||
totalCost: number;
|
||||
startSocPercent?: number;
|
||||
|
||||
@@ -49,15 +49,21 @@ export function validateChargingSession(body: unknown, pathSessionId: string): C
|
||||
});
|
||||
const startSocPercent = record.startSocPercent;
|
||||
const endSocPercent = record.endSocPercent;
|
||||
const energyKwh = bounded(record.energyKwh, 0, 1000, "energyKwh");
|
||||
const pricePerKwh = bounded(record.pricePerKwh, 0, 1_000_000, "pricePerKwh");
|
||||
const chargingLossPercent = 10;
|
||||
const gridEnergyKwh = energyKwh * 1.1;
|
||||
return {
|
||||
installationId: validateUuid(record.installationId, "installationId"),
|
||||
sessionId,
|
||||
startedAt: validateDate(record.startedAt, "startedAt"),
|
||||
endedAt: validateDate(record.endedAt, "endedAt"),
|
||||
durationSeconds: bounded(record.durationSeconds, 0, 604800, "durationSeconds"),
|
||||
energyKwh: bounded(record.energyKwh, 0, 1000, "energyKwh"),
|
||||
pricePerKwh: bounded(record.pricePerKwh, 0, 1_000_000, "pricePerKwh"),
|
||||
totalCost: bounded(record.totalCost, 0, 1_000_000_000, "totalCost"),
|
||||
energyKwh,
|
||||
chargingLossPercent,
|
||||
gridEnergyKwh,
|
||||
pricePerKwh,
|
||||
totalCost: gridEnergyKwh * pricePerKwh,
|
||||
...(startSocPercent === undefined ? {} : { startSocPercent: bounded(startSocPercent, 0, 100, "startSocPercent") }),
|
||||
...(endSocPercent === undefined ? {} : { endSocPercent: bounded(endSocPercent, 0, 100, "endSocPercent") }),
|
||||
curve,
|
||||
|
||||
@@ -52,6 +52,8 @@ test("a charging session must match the id in its path", () => {
|
||||
endedAt: 1_760_002_000_000,
|
||||
durationSeconds: 1800,
|
||||
energyKwh: 31.4,
|
||||
chargingLossPercent: 10,
|
||||
gridEnergyKwh: 34.54,
|
||||
pricePerKwh: 7.5,
|
||||
totalCost: 235.5,
|
||||
startSocPercent: 18,
|
||||
@@ -61,6 +63,9 @@ test("a charging session must match the id in its path", () => {
|
||||
const record = validateChargingSession(body, SESSION);
|
||||
assert.equal(record.curve.length, 1);
|
||||
assert.equal(record.curve[0].powerKw, 84.2);
|
||||
assert.equal(record.chargingLossPercent, 10);
|
||||
assert.equal(record.gridEnergyKwh, 34.54);
|
||||
assert.equal(record.totalCost, 259.05);
|
||||
assert.throws(() => validateChargingSession(body, "11111111-2222-4333-8444-555555555556"), ValidationError);
|
||||
});
|
||||
|
||||
@@ -79,6 +84,8 @@ test("refuses a curve longer than the car can produce", () => {
|
||||
endedAt: 2,
|
||||
durationSeconds: 1,
|
||||
energyKwh: 1,
|
||||
chargingLossPercent: 10,
|
||||
gridEnergyKwh: 1.1,
|
||||
pricePerKwh: 1,
|
||||
totalCost: 1,
|
||||
curve,
|
||||
|
||||
Reference in New Issue
Block a user