fix(control): reg 108 v PV_SURPLUS sleduje charge intent (BA81 nenabíjelo levné ráno)

deye_battery_charge_discharge_amps: v PASSIVE+PV_SURPLUS reg 108 = max když plán
chce nabíjet (bat_w>0) místo tvrdé 0; baterka nabere co zvládne, přebytek nad
nabíjecí rychlost do sítě. + kalibrace: SoC u maxima → dojet na 100% (BMS). Sell
beze změny. Vědomě přepsán test starého chování. 365 passed. Všechny Deye lokality.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Dusan Vojacek
2026-06-16 16:32:01 +02:00
parent 17147ca412
commit daf7ed4d4b
7 changed files with 83 additions and 8 deletions

View File

@@ -17,6 +17,10 @@ from services.control.models import ControlSetpoints, InverterConfig, OperatingM
logger = logging.getLogger(__name__)
#: Tolerance pod max SoC, v rámci níž se v PV přebytku nechá baterka dojet na max
#: (reg 108 = max) kvůli BMS rekalibraci SoC (LiFePO4 potřebuje občas na 100 %).
BATTERY_CALIB_TOPOFF_MARGIN_PCT = 3.0
def _deye_system_time_register_rows() -> tuple[datetime, list[tuple[int, str, int]]]:
"""Hodnoty pro reg 62-64 (Europe/Prague); sekundy v reg 64 = 0 (stabilnější zápis)."""
@@ -437,12 +441,20 @@ def deye_battery_charge_discharge_amps(
max_discharge_a: int,
export_mode: str | None = None,
export_ban: bool = False,
current_soc_pct: float | None = None,
max_soc_pct: int | None = None,
) -> tuple[int | None, int]:
"""
Proud nabíjení / vybíjení (reg 108 / 109) pro zápis Deye.
**PV_SURPLUS** (PASSIVE, export FVE): **108 = 0**, **109 = max** — baterie se přes limit
nabíjení neplní, přebytek jde do sítě (142 = zero-export dle instalace, 145 = 1).
**PV_SURPLUS** (PASSIVE, export FVE) — reg 108 SLEDUJE charge intent plánu (fix 2026-06-16):
- `bat_w > 0` (plán chce nabíjet z přebytku) → **108 = max**: baterie nabere kolik fyzicky
zvládne (nabíjecí rychlost), přebytek NAD ni jde do sítě (BA81: výroba 12 kW > rychlost
6 kW → 6 do baterky, 6 ven). Dřív tvrdě 108=0 i při bat_w>0 → baterka nenabíjela ani
levné ranní PV (control bug).
- kalibrace: SoC u maxima (`>= max_soc margin`) + přebytek → **108 = max**, ať dojede na
100 % (BMS rekalibrace SoC). Strop drží Deye max_soc.
- jen „prodej PV a drž baterku" daleko od maxima (`bat_w <= 0`) → **108 = 0**, přebytek ven.
PASSIVE + nabíjení bez exportního záměru (`battery_w > 0`, export_mode NONE): **108 = max**.
**CHARGE** ze sítě: 108 z `battery_w`.
@@ -464,6 +476,16 @@ def deye_battery_charge_discharge_amps(
export_ban=export_ban,
grid_w=grid_w,
):
# reg 108 sleduje charge intent: nabíjet z přebytku (bat_w>0) nebo dojet na max
# kvůli BMS kalibraci (SoC u maxima + přebytek) → 108 = max; jinak 108 = 0 (přebytek
# ven). Strop SoC drží Deye max_soc, takže 108=max nepřebije nad povolené.
near_full_calib = (
current_soc_pct is not None
and max_soc_pct is not None
and float(current_soc_pct) >= float(max_soc_pct) - BATTERY_CALIB_TOPOFF_MARGIN_PCT
)
if bat_w > 0 or near_full_calib:
return int(max_charge_a), int(max_discharge_a)
return 0, int(max_discharge_a)
if bat_w > 0:
return int(max_charge_a), int(max_discharge_a)