FE implementace deye modu
Some checks failed
CI and deploy / migration-check (push) Failing after 11s
CI and deploy / deploy (push) Has been skipped

This commit is contained in:
Dusan Vojacek
2026-04-20 08:50:20 +02:00
parent 43b594c8d5
commit d8dbb284fd
5 changed files with 109 additions and 8 deletions

View File

@@ -187,6 +187,7 @@ function syntheticForecastOnlyInterval(
battery_setpoint_w: null,
battery_soc_target_pct: null,
grid_setpoint_w: null,
deye_physical_mode: null,
ev1_setpoint_w: null,
ev2_setpoint_w: null,
heat_pump_enabled: null,
@@ -284,8 +285,6 @@ function axiosDetail(e: unknown): string {
function deyeSetpointLabel(i: PlanningIntervalDto): string {
const battery_w = i.battery_setpoint_w ?? 0
const grid_w = i.grid_setpoint_w ?? 0
const is_exporting = battery_w < -500 || grid_w < -500
const is_charging = battery_w > 500
const tgt = i.battery_soc_target_pct
const targetSoc = tgt != null ? Math.min(95, Math.round(Number(tgt))) : 80
@@ -295,14 +294,49 @@ function deyeSetpointLabel(i: PlanningIntervalDto): string {
return `${s}kW`
}
if (is_exporting) {
const pm = (i.deye_physical_mode ?? '').toString().trim().toUpperCase()
if (pm === 'SELL') {
const tpPowerW = Math.abs(battery_w)
return `${fmtKw(tpPowerW)} | reg178 bit45=10 (grid PS off)`
return `SELL | ${fmtKw(tpPowerW)} | reg142=0 reg178=32`
}
if (is_charging) {
return `${fmtKw(battery_w)} | grid=yes | SOC→${targetSoc}%`
if (pm === 'CHARGE') {
return `CHARGE | ${fmtKw(Math.max(0, battery_w))} | grid=yes | SOC→${targetSoc}%`
}
// PASSIVE (ZERO): doplň informaci o variantě 108/109 podle pravidel (bez wattových prahů).
if (grid_w < 0 && battery_w >= 0) return 'PASSIVE | FVE→síť (108=0)'
if (grid_w > 0 && battery_w <= 0) return 'PASSIVE | držet bat. (109=0)'
return 'PASSIVE | max/max'
}
function deyeModeBadge(i: PlanningIntervalDto): { label: string; klass: string; title: string } {
const m = (i.deye_physical_mode ?? 'PASSIVE').toString().trim().toUpperCase()
const battery_w = i.battery_setpoint_w ?? 0
const grid_w = i.grid_setpoint_w ?? 0
if (m === 'SELL') {
return {
label: 'SELL',
klass: 'bg-orange-500/15 text-orange-200 ring-1 ring-orange-500/35',
title: 'SELL (selling first): reg142=0, reg178=32 (grid peak shaving off)',
}
}
if (m === 'CHARGE') {
return {
label: 'CHARGE',
klass: 'bg-sky-500/15 text-sky-200 ring-1 ring-sky-500/35',
title: 'CHARGE (grid charge): TOU grid_charge enabled v time pointech; reg178=48',
}
}
let variant = 'max/max'
if (grid_w < 0 && battery_w >= 0) variant = 'FVE→síť (108=0)'
else if (grid_w > 0 && battery_w <= 0) variant = 'držet bat. (109=0)'
return {
label: 'PASSIVE',
klass: 'bg-slate-600/40 text-slate-200 ring-1 ring-slate-500/30',
title: `PASSIVE (ZERO): ${variant}; reg142=deye_zero_export_mode; reg178=48`,
}
return '~ 2kW | hold'
}
function tableRowClass(
@@ -1114,7 +1148,22 @@ export default function Planning() {
<CenaCell i={i} nowMs={nowMs} />
<td className="pr-2 font-mono tabular-nums text-slate-300">{i.battery_setpoint_w ?? '—'}</td>
<td className="max-w-[200px] whitespace-normal break-words pr-2 text-slate-300">
{deyeSetpointLabel(i)}
<div className="flex flex-wrap items-center gap-2">
{(() => {
const b = deyeModeBadge(i)
return (
<span
className={`inline-flex items-center rounded-md px-2 py-0.5 text-[10px] font-semibold ${b.klass}`}
title={b.title}
>
{b.label}
</span>
)
})()}
<span className="text-slate-400" title={deyeSetpointLabel(i)}>
{deyeSetpointLabel(i)}
</span>
</div>
</td>
<td className="pr-2 font-mono tabular-nums text-slate-300">
{i.battery_soc_target_pct != null