do flow pridana ekonomika
All checks were successful
deploy / deploy (push) Successful in 1m21s
test / smoke-test (push) Successful in 5s

This commit is contained in:
Dusan Vojacek
2026-04-10 23:06:25 +02:00
parent 44ab3783ce
commit b50041cfc7
4 changed files with 119 additions and 4 deletions

View File

@@ -41,6 +41,11 @@ function kwh(v: number | null | undefined, d = 2): string {
return v.toFixed(d)
}
function czk(v: number | null | undefined): string {
if (v == null) return ''
return v.toLocaleString('cs-CZ', { minimumFractionDigits: 2, maximumFractionDigits: 2 })
}
function aggregateFlows(days: DailyEnergyFlows[]): FlowTotals & {
pv_production_kwh: number
grid_import_kwh: number
@@ -48,6 +53,10 @@ function aggregateFlows(days: DailyEnergyFlows[]): FlowTotals & {
batt_charge_kwh: number
batt_discharge_kwh: number
load_kwh: number
grid_import_cashflow_czk: number
grid_export_revenue_czk: number
grid_to_load_cost_czk: number
grid_to_batt_cost_czk: number
} {
const z = {
pv_production_kwh: 0,
@@ -63,6 +72,10 @@ function aggregateFlows(days: DailyEnergyFlows[]): FlowTotals & {
batt_to_grid_kwh: 0,
grid_to_load_kwh: 0,
grid_to_batt_kwh: 0,
grid_import_cashflow_czk: 0,
grid_export_revenue_czk: 0,
grid_to_load_cost_czk: 0,
grid_to_batt_cost_czk: 0,
}
for (const d of days) {
z.pv_production_kwh += d.pv_production_kwh
@@ -78,6 +91,10 @@ function aggregateFlows(days: DailyEnergyFlows[]): FlowTotals & {
z.batt_to_grid_kwh += d.batt_to_grid_kwh
z.grid_to_load_kwh += d.grid_to_load_kwh
z.grid_to_batt_kwh += d.grid_to_batt_kwh
z.grid_import_cashflow_czk += d.grid_import_cashflow_czk ?? 0
z.grid_export_revenue_czk += d.grid_export_revenue_czk ?? 0
z.grid_to_load_cost_czk += d.grid_to_load_cost_czk ?? 0
z.grid_to_batt_cost_czk += d.grid_to_batt_cost_czk ?? 0
}
return z
}
@@ -167,6 +184,17 @@ export default function EnergyFlows() {
? Math.min(100, (totals.batt_discharge_kwh / totals.batt_charge_kwh) * 100)
: null
const avgImportKcPerKwh =
totals && totals.grid_import_kwh > 0.001
? totals.grid_import_cashflow_czk / totals.grid_import_kwh
: null
const avgExportKcPerKwh =
totals && totals.grid_export_kwh > 0.001
? totals.grid_export_revenue_czk / totals.grid_export_kwh
: null
const gridNetCzk =
totals != null ? totals.grid_import_cashflow_czk - totals.grid_export_revenue_czk : null
return (
<main className="mx-auto max-w-7xl space-y-6 px-4 py-6 md:px-8">
{siteReady && siteRow && (
@@ -239,7 +267,7 @@ export default function EnergyFlows() {
)}
{totals && (
<div className="grid gap-3 sm:grid-cols-2 xl:grid-cols-4">
<div className="grid gap-3 sm:grid-cols-2 xl:grid-cols-5">
<div className="rounded-xl border border-slate-800 bg-slate-900 p-4">
<p className="text-xs font-medium uppercase tracking-wide text-amber-400">Perspektiva FVE</p>
<p className="mt-2 text-2xl font-semibold text-white">{totals.pv_production_kwh.toFixed(1)} kWh</p>
@@ -296,6 +324,50 @@ export default function EnergyFlows() {
zaokrouhlení po 15min intervalech.
</p>
</div>
<div className="rounded-xl border border-slate-800 bg-slate-900 p-4">
<p className="text-xs font-medium uppercase tracking-wide text-rose-400">Perspektiva financí</p>
<p className="mt-2 text-sm text-slate-400">
Nákup ze sítě:{' '}
<span className="font-semibold text-red-200">{czk(totals.grid_import_cashflow_czk)}</span>
</p>
<p className="mt-1 text-sm text-slate-400">
Prodej do sítě:{' '}
<span className="font-semibold text-green-200">{czk(totals.grid_export_revenue_czk)}</span>
</p>
<p className="mt-1 text-sm text-slate-300">
Bilance sítě (nákup prodej):{' '}
<span className="font-semibold text-white">{czk(gridNetCzk)}</span>
</p>
<ul className="mt-2 space-y-1 border-t border-slate-800 pt-2 text-xs text-slate-400">
<li>
Prům. cena nákupu:{' '}
{avgImportKcPerKwh != null ? (
<span className="text-rose-200/90">{avgImportKcPerKwh.toFixed(3)} /kWh</span>
) : (
<span></span>
)}
</li>
<li>
Prům. cena prodeje:{' '}
{avgExportKcPerKwh != null ? (
<span className="text-emerald-200/90">{avgExportKcPerKwh.toFixed(3)} /kWh</span>
) : (
<span></span>
)}
</li>
</ul>
<p className="mt-2 text-[11px] font-medium uppercase tracking-wide text-slate-500">
Rozpad nákladů importu (efektivní cena × modelovaný tok)
</p>
<ul className="mt-1 space-y-1 text-xs text-slate-400">
<li>Do spotřeby: {czk(totals.grid_to_load_cost_czk)} </li>
<li>Do baterie: {czk(totals.grid_to_batt_cost_czk)} </li>
</ul>
<p className="mt-2 border-t border-slate-800 pt-2 text-[11px] leading-snug text-slate-500">
Stejná jednotková cena v každém 15min slotu; součet rozpadu se může mírně lišit od celkového nákupu kvůli
zaokrouhlení a odchylce modelu toků od měřeného importu.
</p>
</div>
</div>
)}

View File

@@ -14,6 +14,10 @@ export type DailyEnergyFlows = {
batt_to_grid_kwh: number
grid_to_load_kwh: number
grid_to_batt_kwh: number
grid_import_cashflow_czk: number
grid_export_revenue_czk: number
grid_to_load_cost_czk: number
grid_to_batt_cost_czk: number
}
export type DailyEnergyFlowsResponse = {