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>
145 lines
4.7 KiB
Python
145 lines
4.7 KiB
Python
"""PASSIVE + PV_SURPLUS: reg 108 sleduje charge intent (fix 2026-06-16).
|
|
|
|
bat_w>0 (plán chce nabíjet z přebytku) → 108=max (baterka nabere co zvládne, zbytek ven);
|
|
SoC u maxima + přebytek → 108=max (BMS kalibrace na 100 %); jen "prodej PV a drž baterku"
|
|
daleko od maxima (bat_w<=0) → 108=0. 109=max, 142 zůstává zero-export (1/2).
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import unittest
|
|
|
|
from services.control.setpoints import deye_battery_charge_discharge_amps
|
|
|
|
|
|
class PassivePvSurplusChargeAmpsTests(unittest.TestCase):
|
|
def test_pv_surplus_export_zeros_charge_amps(self) -> None:
|
|
ch, dis = deye_battery_charge_discharge_amps(
|
|
lock_battery=False,
|
|
deye_mode="PASSIVE",
|
|
self_sustain_local_use=False,
|
|
bat_w=-177,
|
|
grid_w=-2851,
|
|
max_charge_a=100,
|
|
max_discharge_a=90,
|
|
export_mode="PV_SURPLUS",
|
|
export_ban=False,
|
|
)
|
|
self.assertEqual(ch, 0)
|
|
self.assertEqual(dis, 90)
|
|
|
|
def test_pv_surplus_with_positive_battery_w_charges_at_max(self) -> None:
|
|
"""Fix 2026-06-16: plán chce nabíjet z přebytku (bat_w>0) → 108=max (ne 0).
|
|
|
|
Baterka nabere kolik zvládne, přebytek nad nabíjecí rychlost jde do sítě (BA81).
|
|
"""
|
|
ch, dis = deye_battery_charge_discharge_amps(
|
|
lock_battery=False,
|
|
deye_mode="PASSIVE",
|
|
self_sustain_local_use=False,
|
|
bat_w=5000,
|
|
grid_w=-2000,
|
|
max_charge_a=100,
|
|
max_discharge_a=100,
|
|
export_mode="PV_SURPLUS",
|
|
export_ban=False,
|
|
)
|
|
self.assertEqual(ch, 100)
|
|
self.assertEqual(dis, 100)
|
|
|
|
def test_pv_surplus_near_full_tops_off_for_calibration(self) -> None:
|
|
"""SoC u maxima (97 >= 100-3) + přebytek → 108=max i při bat_w<=0 (BMS kalibrace)."""
|
|
ch, dis = deye_battery_charge_discharge_amps(
|
|
lock_battery=False,
|
|
deye_mode="PASSIVE",
|
|
self_sustain_local_use=False,
|
|
bat_w=0,
|
|
grid_w=-2000,
|
|
max_charge_a=100,
|
|
max_discharge_a=100,
|
|
export_mode="PV_SURPLUS",
|
|
export_ban=False,
|
|
current_soc_pct=97.0,
|
|
max_soc_pct=100,
|
|
)
|
|
self.assertEqual(ch, 100)
|
|
|
|
def test_pv_surplus_sell_hold_far_from_full_zeros_charge(self) -> None:
|
|
"""Prodej PV a drž baterku daleko od maxima (bat_w<=0, SoC nízko) → 108=0."""
|
|
ch, dis = deye_battery_charge_discharge_amps(
|
|
lock_battery=False,
|
|
deye_mode="PASSIVE",
|
|
self_sustain_local_use=False,
|
|
bat_w=0,
|
|
grid_w=-2000,
|
|
max_charge_a=100,
|
|
max_discharge_a=100,
|
|
export_mode="PV_SURPLUS",
|
|
export_ban=False,
|
|
current_soc_pct=60.0,
|
|
max_soc_pct=100,
|
|
)
|
|
self.assertEqual(ch, 0)
|
|
self.assertEqual(dis, 100)
|
|
|
|
def test_passive_charge_without_export_mode_uses_max_108(self) -> None:
|
|
ch, dis = deye_battery_charge_discharge_amps(
|
|
lock_battery=False,
|
|
deye_mode="PASSIVE",
|
|
self_sustain_local_use=False,
|
|
bat_w=5000,
|
|
grid_w=0,
|
|
max_charge_a=100,
|
|
max_discharge_a=100,
|
|
export_mode="NONE",
|
|
export_ban=False,
|
|
)
|
|
self.assertEqual(ch, 100)
|
|
self.assertEqual(dis, 100)
|
|
|
|
def test_legacy_negative_grid_infers_pv_surplus(self) -> None:
|
|
ch, dis = deye_battery_charge_discharge_amps(
|
|
lock_battery=False,
|
|
deye_mode="PASSIVE",
|
|
self_sustain_local_use=False,
|
|
bat_w=0,
|
|
grid_w=-2000,
|
|
max_charge_a=100,
|
|
max_discharge_a=100,
|
|
export_mode=None,
|
|
export_ban=False,
|
|
)
|
|
self.assertEqual(ch, 0)
|
|
self.assertEqual(dis, 100)
|
|
|
|
def test_charge_mode_still_scales_108_from_battery_w(self) -> None:
|
|
ch, dis = deye_battery_charge_discharge_amps(
|
|
lock_battery=False,
|
|
deye_mode="CHARGE",
|
|
self_sustain_local_use=False,
|
|
bat_w=2000,
|
|
grid_w=3000,
|
|
max_charge_a=100,
|
|
max_discharge_a=100,
|
|
)
|
|
self.assertLess(ch, 100)
|
|
self.assertGreater(ch, 0)
|
|
self.assertEqual(dis, 0)
|
|
|
|
def test_sell_skips_charge_amp_write(self) -> None:
|
|
ch, dis = deye_battery_charge_discharge_amps(
|
|
lock_battery=False,
|
|
deye_mode="SELL",
|
|
self_sustain_local_use=False,
|
|
bat_w=-3000,
|
|
grid_w=-2000,
|
|
max_charge_a=100,
|
|
max_discharge_a=80,
|
|
)
|
|
self.assertIsNone(ch)
|
|
self.assertEqual(dis, 80)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|