Count driving only and include charging losses
This commit is contained in:
+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