narovnani spravneho rezimu - nastavenim charge A
This commit is contained in:
@@ -108,7 +108,7 @@ Projekt je **SQL-first**: doménová logika, agregace, joiny mezi tabulkami a st
|
||||
|
||||
17. **Modbus zápis = journal.** Každý zápis do zařízení přes control exporter se loguje do `ems.modbus_command`. **Verifikační job** běží každé **2 minuty** a ověřuje nedávno zápis (`written` → čtení registru). Při **mismatch** po max. **3** pokusech o zápis → u běžných registrů přepnutí na **SELF_SUSTAIN** (`run_fn_set_mode_with_discord` → `fn_set_mode`, `activated_by` = `system:mismatch`) + **Discord** při skutečné změně režimu. **Výjimka:** souvislý blok Deye **62–64** (čas) → po 3 neúspěšných ověřeních **bez** změny režimu, kritický **Discord** (`notify_modbus_clock_verify_exhausted`). **Obecně:** při jakékoli změně `mode_code` z Pythonu (`POST /api/v1/sites/{id}/mode`, mismatch → SELF_SUSTAIN, `fn_expire_modes`) lze Discord zapnout přes `DISCORD_WEBHOOK_URL`. Detail: `docs/04-modules/modbus-command-journal.md`.
|
||||
|
||||
18. **Deye zápis registrů 60–499:** pouze **FC 0x10** (`write_registers`), **nikdy** FC 0x06 pro tento rozsah; **`execute_modbus_commands`** slučuje souvislé adresy do jednoho FC 0x10. **Fyzický režim Deye** (`PASSIVE` / `CHARGE` / `SELL`): **výhradně** `get_deye_mode` v `exporter_monolith.py` (bez wattových prahů: **SELL** při `battery_w` < 0 a `grid_setpoint_w` < 0; **CHARGE** při obou > 0; jinak **PASSIVE**). **PASSIVE (ZERO, AUTO):** **108/109** dle `deye_battery_charge_discharge_amps` a `_deye_zero_export_amps_for_passive` (jen asymetrie **import bez vybíjení** → **109 = 0**; export **108** nenuluje); **TOU SOC** (reg 166+): **PASSIVE** = **`min_soc_percent`**, **CHARGE** = **`max_soc_percent`** (clamp 10–100 z DB), **SELL** = **`reserve_soc_percent`** (`_deye_passive_tou_battery_soc_pct`, `_deye_tou_params`). **SELL:** 108=0, 109=max, **178**=32 (peak shaving off), **143** omezeno podle `|grid_setpoint_w|`; **142/145/TOU** jako v `write_inverter_setpoints`. **Reg 340** (*max solar power*, W): jen pokud `ems.fn_site_has_active_green_bonus_pv(site_id)` (lokalita se zeleným bonusem na PV poli) **a** `ems.fn_inverter_pv_a_max_w(inverter_id) > 0`; hodnota z `pv_a_forecast_solver_w` / `pv_a_curtailed_w` (AUTO). **Není** v `DEYE_CRITICAL_REGS_SELF_SUSTAIN` — verify mismatch nečeká přepnutí do SELF_SUSTAIN. **PRESERVE:** `lock_battery` → 108/109=0. **Čas 62–64**, bloky TOU **1–2** vs **3–6**, verify, Discord: beze změny oproti dřívějšímu chování — plný popis **`docs/04-modules/modbus-registers.md`** a **`docs/04-modules/operating-modes.md`**.
|
||||
18. **Deye zápis registrů 60–499:** pouze **FC 0x10** (`write_registers`), **nikdy** FC 0x06 pro tento rozsah; **`execute_modbus_commands`** slučuje souvislé adresy do jednoho FC 0x10. **Fyzický režim Deye** (`PASSIVE` / `CHARGE` / `SELL`): **výhradně** `get_deye_mode` v `exporter_monolith.py` (bez wattových prahů: **SELL** při `battery_w` < 0 a `grid_setpoint_w` < 0; **CHARGE** při obou > 0; jinak **PASSIVE**). **PASSIVE (ZERO, AUTO):** **`export_mode=PV_SURPLUS`** → **108=0**, **109=max**, **142**=`deye_zero_export_mode` (ne selling first); jinak **108/109** dle `deye_battery_charge_discharge_amps` / `_deye_zero_export_amps_for_passive` (import bez vybíjení → **109=0**); **TOU SOC** (reg 166+): **PASSIVE** = **`min_soc_percent`**, **CHARGE** = **`max_soc_percent`** (clamp 10–100 z DB), **SELL** = **`reserve_soc_percent`** (`_deye_passive_tou_battery_soc_pct`, `_deye_tou_params`). **SELL:** 108=0, 109=max, **178**=32 (peak shaving off), **143** omezeno podle `|grid_setpoint_w|`; **142/145/TOU** jako v `write_inverter_setpoints`. **Reg 340** (*max solar power*, W): jen pokud `ems.fn_site_has_active_green_bonus_pv(site_id)` (lokalita se zeleným bonusem na PV poli) **a** `ems.fn_inverter_pv_a_max_w(inverter_id) > 0`; hodnota z `pv_a_forecast_solver_w` / `pv_a_curtailed_w` (AUTO). **Není** v `DEYE_CRITICAL_REGS_SELF_SUSTAIN` — verify mismatch nečeká přepnutí do SELF_SUSTAIN. **PRESERVE:** `lock_battery` → 108/109=0. **Čas 62–64**, bloky TOU **1–2** vs **3–6**, verify, Discord: beze změny oproti dřívějšímu chování — plný popis **`docs/04-modules/modbus-registers.md`** a **`docs/04-modules/operating-modes.md`**.
|
||||
|
||||
19. **Baterie – export v LP:** V `solve_dispatch` binárka `z_export[t]`: pokud `grid_export` v daném slotu **≥ 1** W, platí koncové `soc[t] ≥ arb_base_wh` (ekonomická rezerva z DB, ne časová řada `arb_floor_series`). Bez exportu může plán jít k `min_soc_percent` (provozní podlaha; u paralelních packů často 11–12 %, migrace V029 + komentář sloupce).
|
||||
|
||||
|
||||
@@ -37,7 +37,6 @@ from services.control.modbus_journal import (
|
||||
from services.control.models import ControlSetpoints
|
||||
from services.control.repository import _get_current_soc, _load_inverter_config
|
||||
from services.control.setpoints import (
|
||||
_deye_reg142_limit_control,
|
||||
_deye_reg143_export_w,
|
||||
_deye_system_time_register_rows,
|
||||
_deye_time_point_rows,
|
||||
@@ -89,16 +88,12 @@ async def write_inverter_setpoints(
|
||||
grid_w=grid_w,
|
||||
max_charge_a=int(inv.max_charge_a),
|
||||
max_discharge_a=int(inv.max_discharge_a),
|
||||
export_mode=setpoints_now.export_mode,
|
||||
export_ban=bool(setpoints_now.export_ban),
|
||||
)
|
||||
|
||||
zero_exp_mode = int(inv.deye_zero_export_mode or 1)
|
||||
selling_mode = _deye_reg142_limit_control(
|
||||
deye_mode=deye_mode,
|
||||
grid_w=grid_w,
|
||||
export_ban=bool(setpoints_now.export_ban),
|
||||
export_mode=setpoints_now.export_mode,
|
||||
zero_export_mode=zero_exp_mode,
|
||||
)
|
||||
selling_mode = 0 if deye_mode == "SELL" else zero_exp_mode
|
||||
solar_sell = 0 if (setpoints_now.export_ban and deye_mode != "SELL") else 1
|
||||
export_limit = export_lim
|
||||
reg178_val = REG178_SELL if deye_mode == "SELL" else REG178_PASSIVE
|
||||
|
||||
@@ -217,28 +217,25 @@ def _deye_reg143_export_w(no_export: bool, max_export_power_w: int | None) -> in
|
||||
return max(0, int(max_export_power_w or 0))
|
||||
|
||||
|
||||
def _deye_reg142_limit_control(
|
||||
def _is_passive_pv_surplus_export(
|
||||
*,
|
||||
deye_mode: str,
|
||||
grid_w: int,
|
||||
export_ban: bool,
|
||||
export_mode: str | None,
|
||||
zero_export_mode: int,
|
||||
) -> int:
|
||||
export_ban: bool,
|
||||
grid_w: int,
|
||||
) -> bool:
|
||||
"""
|
||||
Reg 142: 0 = selling first, 1/2 = zero export (load / CT).
|
||||
Plán s exportem (záporný grid_setpoint, bez export_ban) musí povolit prodej FVE do sítě
|
||||
i v PASSIVE — jinak CT instalace (deye_zero_export_mode=2) drží přebytek v baterii.
|
||||
Přetok FVE do sítě v PASSIVE (ne SELL z baterie): reg 142 zůstane zero-export (1/2),
|
||||
nabíjení se blokuje přes **108 = 0** — baterie nemá kam brát přebytek → jde do sítě (145).
|
||||
"""
|
||||
if deye_mode == "SELL":
|
||||
return 0
|
||||
if deye_mode != "PASSIVE" or export_ban:
|
||||
return False
|
||||
em = (export_mode or "").strip().upper()
|
||||
if export_ban or em == "NONE" or grid_w >= 0:
|
||||
return int(zero_export_mode)
|
||||
if em in {"PV_SURPLUS", "BATTERY_SELL"}:
|
||||
return 0
|
||||
# starší řádky bez export_mode: záporný grid_setpoint = export záměr
|
||||
return 0
|
||||
if em == "PV_SURPLUS":
|
||||
return True
|
||||
if em in {"NONE", "BATTERY_SELL"}:
|
||||
return False
|
||||
return grid_w < 0
|
||||
|
||||
|
||||
def _clamp_deye_tou_soc_pct(pct: int) -> int:
|
||||
@@ -273,8 +270,8 @@ def _deye_zero_export_amps_for_passive(
|
||||
"""
|
||||
PASSIVE (zero export k CT/zátěži): asymetrie jen tam, kde dává smysl pro import.
|
||||
|
||||
Export do sítě (grid_w < 0) už směr toku řeší režim / 142 / 145 — **108** jako strop zbytečně
|
||||
nenulovat na 0 (viz home-01). Jediná speciální větev: import bez nabíjení → vypnout vybíjení.
|
||||
Přetok FVE do sítě řeší větev ``_is_passive_pv_surplus_export`` (**108 = 0**). Zde jen import
|
||||
bez nabíjení → vypnout vybíjení (**109 = 0**).
|
||||
"""
|
||||
if grid_w > 0 and bat_w <= 0:
|
||||
return max_charge_a, 0
|
||||
@@ -290,14 +287,17 @@ def deye_battery_charge_discharge_amps(
|
||||
grid_w: int,
|
||||
max_charge_a: int,
|
||||
max_discharge_a: int,
|
||||
export_mode: str | None = None,
|
||||
export_ban: bool = False,
|
||||
) -> tuple[int, int]:
|
||||
"""
|
||||
Proud nabíjení / vybíjení (reg 108 / 109) pro zápis Deye.
|
||||
|
||||
PASSIVE + plán chce nabíjet (typicky z FVE, i při exportu zbytku do sítě): **108 = max_charge_a**
|
||||
z invertoru — reg. 108 je strop, ne příkaz k proudu; průměrný `battery_w` ze slotu nesmí špičku FVE
|
||||
stíhat do baterie omezovat. **109 = max_discharge_a** (domácnost z baterie při výpadku PV).
|
||||
**CHARGE** (import ze sítě + nabíjení): 108 dál z `battery_w` (řízený importní okamžik). **SELL** beze změny.
|
||||
**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).
|
||||
|
||||
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`. **SELL**: 108 = 0, 109 = max.
|
||||
"""
|
||||
if lock_battery:
|
||||
return 0, 0
|
||||
@@ -307,6 +307,13 @@ def deye_battery_charge_discharge_amps(
|
||||
return 0, int(max_discharge_a)
|
||||
if self_sustain_local_use:
|
||||
return int(max_charge_a), int(max_discharge_a)
|
||||
if _is_passive_pv_surplus_export(
|
||||
deye_mode=deye_mode,
|
||||
export_mode=export_mode,
|
||||
export_ban=export_ban,
|
||||
grid_w=grid_w,
|
||||
):
|
||||
return 0, int(max_discharge_a)
|
||||
if bat_w > 0:
|
||||
return int(max_charge_a), int(max_discharge_a)
|
||||
return _deye_zero_export_amps_for_passive(
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
"""PASSIVE + plán chce nabíjet: 108 = plný strop z DB, 109 = max (PV špička + domácnost)."""
|
||||
"""PASSIVE + PV_SURPLUS: 108=0 (nepoužívat baterii), 109=max; 142 zůstává zero-export (1/2)."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
@@ -8,7 +8,23 @@ from services.control.setpoints import deye_battery_charge_discharge_amps
|
||||
|
||||
|
||||
class PassivePvSurplusChargeAmpsTests(unittest.TestCase):
|
||||
def test_passive_charge_while_exporting_grid_negative(self) -> None:
|
||||
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_even_if_lp_shows_positive_battery_w(self) -> None:
|
||||
"""Plán může mít kladný battery_w; exportní záměr je PV_SURPLUS → 108=0."""
|
||||
ch, dis = deye_battery_charge_discharge_amps(
|
||||
lock_battery=False,
|
||||
deye_mode="PASSIVE",
|
||||
@@ -17,12 +33,43 @@ class PassivePvSurplusChargeAmpsTests(unittest.TestCase):
|
||||
grid_w=-2000,
|
||||
max_charge_a=100,
|
||||
max_discharge_a=100,
|
||||
export_mode="PV_SURPLUS",
|
||||
export_ban=False,
|
||||
)
|
||||
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:
|
||||
"""CHARGE (síť + baterie): 108 podle plánu, ne vždy plný strop."""
|
||||
ch, dis = deye_battery_charge_discharge_amps(
|
||||
lock_battery=False,
|
||||
deye_mode="CHARGE",
|
||||
@@ -36,19 +83,6 @@ class PassivePvSurplusChargeAmpsTests(unittest.TestCase):
|
||||
self.assertGreater(ch, 0)
|
||||
self.assertEqual(dis, 0)
|
||||
|
||||
def test_passive_export_without_battery_charge_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=0,
|
||||
grid_w=-2000,
|
||||
max_charge_a=100,
|
||||
max_discharge_a=100,
|
||||
)
|
||||
self.assertEqual(ch, 100)
|
||||
self.assertEqual(dis, 100)
|
||||
|
||||
def test_sell_unchanged(self) -> None:
|
||||
ch, dis = deye_battery_charge_discharge_amps(
|
||||
lock_battery=False,
|
||||
|
||||
@@ -17,7 +17,6 @@ from services.control.exporter_monolith import (
|
||||
from services.control.models import OperatingModeInfo
|
||||
from services.control.setpoints import (
|
||||
_build_setpoints,
|
||||
_deye_reg142_limit_control,
|
||||
_deye_zero_export_amps_for_passive,
|
||||
)
|
||||
|
||||
@@ -115,43 +114,6 @@ class DeyeTouParamsTests(unittest.TestCase):
|
||||
)
|
||||
self.assertEqual(get_deye_mode(sp), "PASSIVE")
|
||||
|
||||
def test_reg142_pv_surplus_on_ct_site_uses_selling_first(self) -> None:
|
||||
"""KV1/BA81: PASSIVE + plánovaný export FVE nesmí nechat reg142=2 (zero export CT)."""
|
||||
self.assertEqual(
|
||||
_deye_reg142_limit_control(
|
||||
deye_mode="PASSIVE",
|
||||
grid_w=-3000,
|
||||
export_ban=False,
|
||||
export_mode="PV_SURPLUS",
|
||||
zero_export_mode=2,
|
||||
),
|
||||
0,
|
||||
)
|
||||
|
||||
def test_reg142_no_export_when_export_ban(self) -> None:
|
||||
self.assertEqual(
|
||||
_deye_reg142_limit_control(
|
||||
deye_mode="PASSIVE",
|
||||
grid_w=-1000,
|
||||
export_ban=True,
|
||||
export_mode="PV_SURPLUS",
|
||||
zero_export_mode=2,
|
||||
),
|
||||
2,
|
||||
)
|
||||
|
||||
def test_reg142_legacy_negative_grid_without_export_mode(self) -> None:
|
||||
self.assertEqual(
|
||||
_deye_reg142_limit_control(
|
||||
deye_mode="PASSIVE",
|
||||
grid_w=-2500,
|
||||
export_ban=False,
|
||||
export_mode=None,
|
||||
zero_export_mode=2,
|
||||
),
|
||||
0,
|
||||
)
|
||||
|
||||
def test_build_setpoints_uses_explicit_export_limit(self) -> None:
|
||||
mode = OperatingModeInfo(
|
||||
mode_code="AUTO",
|
||||
|
||||
@@ -12,9 +12,13 @@
|
||||
|
||||
### `export_mode` / `export_limit_w` (V078+)
|
||||
|
||||
Solver ukládá do `planning_interval` záměr exportu (`NONE` / `PV_SURPLUS` / `BATTERY_SELL`) a cap `export_limit_w`. Control exporter podle toho nastaví **reg 142** a **reg 143** — u CT instalací (`deye_zero_export_mode = 2`) musí být při plánovaném exportu FVE **reg 142 = 0** (selling first), jinak invertor drží přebytek v bateri místo v síti.
|
||||
Solver ukládá záměr exportu (`NONE` / `PV_SURPLUS` / `BATTERY_SELL`) a cap `export_limit_w`. U **`PV_SURPLUS`** (přetok FVE, ne prodej z baterie):
|
||||
|
||||
Implementace: `setpoints._deye_reg142_limit_control`, `inverter.write_inverter_setpoints`. Ověření: log `reg142=0` při `export_mode=PV_SURPLUS`, audit `flow_pv_to_grid_wh` > 0.
|
||||
- **reg 142** = `deye_zero_export_mode` z DB (KV1/BA81 typicky **2** — zero export k CT/zátěži; **ne** selling first)
|
||||
- **reg 108 = 0**, **reg 109 = max** — baterie se přes limit nabíjení neplní, přebytek jde do sítě (**145 = 1**)
|
||||
- **reg 143** z `export_limit_w` / site cap
|
||||
|
||||
Implementace: `setpoints._is_passive_pv_surplus_export`, `deye_battery_charge_discharge_amps`. Ověření: log `reg142=2`, `charge_a=0` při `export_mode=PV_SURPLUS`.
|
||||
|
||||
---
|
||||
|
||||
@@ -158,7 +162,7 @@ bits 0–1). Detail registrů: [`modbus-registers.md`](modbus-registers.md) (reg
|
||||
| **CHARGE** | `battery_w` > 0 **a** `grid_setpoint_w` > 0 |
|
||||
| **PASSIVE (ZERO)** | vše ostatní — reg. **108/109** z `deye_battery_charge_discharge_amps()` v `setpoints.py` (volá `write_inverter_setpoints`) |
|
||||
|
||||
**PASSIVE** (AUTO, ZERO): proudy **108/109** počítá **`deye_battery_charge_discharge_amps`**: pokud plán žádá **nabíjení** (`battery_w > 0`) a režim zůstává **PASSIVE** (typicky FVE přebytek, často i **export** části výroby), **108 = max_charge_a z invertoru** — jde o **horní limit** proudu do baterie; průměrný `battery_w` ze 15min slotu nesmí špičku FVE do baterie uměle omezovat (dřívější odvod z W dával smysl jen u **CHARGE** ze sítě). **109 = max z DB**. Když plán nabíjení nechce (`battery_w ≤ 0`) a současně je export, **108** zůstává **max** (přebytek do sítě řeší **142/145**, ne vynucení **108 = 0**). **TOU** z plánu. Reg. **142** = `deye_zero_export_mode`. Reg. **143** je tvrdý limit exportu z lokality/invertoru (ne forecast). Reg. **145** (solar sell): **0** při `export_ban` mimo SELL, jinak **1** — viz [`operating-modes.md`](operating-modes.md) (sekce *ZERO a zakázaný export*).
|
||||
**PASSIVE** (AUTO, ZERO): **`export_mode = PV_SURPLUS`** → **108 = 0**, **109 = max**, **142** = `deye_zero_export_mode` (selling first **jen** u **SELL** z baterie). **`export_mode = NONE`** a `battery_w > 0` (nabíjení z FVE, záporná vykupní) → **108 = max**. Reg. **145**: **0** při `export_ban`, jinak **1**. Reg. **143** = tvrdý cap z plánu/lokality.
|
||||
|
||||
**SELF_SUSTAIN** zůstává **PASSIVE** v `get_deye_mode`; **108/109** jsou vždy **max z DB** (bez variant ZERO). Rozdíl je **`self_sustain_local_use=True`**: **TOU SOC** = **`min_soc_percent`**, `battery_w=None`.
|
||||
|
||||
|
||||
@@ -66,7 +66,7 @@ Vychází z **`grid_setpoint_w`** a **`battery_w`** z `ControlSetpoints` (aktivn
|
||||
|
||||
Režim **CHARGE_CHEAP** nastaví oba setpointy na stejný kladný výkon (min. 1 W), aby byl výsledek **CHARGE**.
|
||||
|
||||
**PASSIVE (ZERO):** reg. **108/109** podle `_deye_zero_export_amps_for_passive` / `deye_battery_charge_discharge_amps` — **108** a **109** jsou typicky **max** z DB; výjimka jen **import bez nabíjení** (`109 = 0`). Export bez kladného `battery_w` už **108 nenuluje** (přebytek do sítě řeší režim / 142 / 145, ne falešné „baterie plná“). Detail: `operating-modes.md`.
|
||||
**PASSIVE (ZERO):** u slotu **`export_mode = PV_SURPLUS`** exporter nastaví **108 = 0** (nabíjecí proud), **109 = max** — baterie nemá kam brát přebytek FVE, jde do sítě při **145 = 1**; **142** zůstává **`deye_zero_export_mode`** (u CT často **2** = zero export k měření zátěže, ne selling first z baterie). Detail: `operating-modes.md`.
|
||||
|
||||
### BA81: GEN port cut-off (reg 178 bits0–1) z plánu
|
||||
|
||||
@@ -97,7 +97,7 @@ Solver předvybírá sloty pro nabíjení a export-vybíjení (`_select_charge_s
|
||||
| **Deye mode** | CHARGE | PASSIVE | SELL | PASSIVE |
|
||||
| **108** charge A | dle `bat_w` | **0** při exportu bez vybíjení | **0** | max nebo **0** dle varianty |
|
||||
| **109** discharge A | **0** | max | **max** | **0** při importu bez nabíjení, jinak max |
|
||||
| **142** limit control | `deye_zero_export_mode` (1 nebo 2) | **0** (selling first) pokud plán exportuje (`export_mode` PV_SURPLUS / záporný `grid_setpoint_w`, bez `export_ban`); jinak `deye_zero_export_mode` | **0** (selling first) | `deye_zero_export_mode` (1 nebo 2) |
|
||||
| **142** limit control | `deye_zero_export_mode` (1 nebo 2) | **`deye_zero_export_mode`** (1/2 = zero export k load/CT; **ne** „blokace do sítě“). Přetok FVE do sítě: **108=0**, **145=1** | **0** (selling first) | `deye_zero_export_mode` (1 nebo 2) |
|
||||
| **145** solar sell | **1** (enabled) | **1** (enabled) | **1** (enabled) | **1** (enabled) |
|
||||
| **178** peak shaving | 48 (PASSIVE) | 48 (PASSIVE) | **32** (SELL) | 48 (PASSIVE) |
|
||||
| **143** export limit | max export W z DB | max export W z DB | max export W z DB | max export W z DB |
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
- **Žádné wattové prahy pro výběr SELL / CHARGE** — mapování z MILP setpointů je čistě ze **znamének** `battery_setpoint_w` a `grid_setpoint_w` (viz `get_deye_mode` v `exporter_monolith.py`).
|
||||
- **Přetok FVE do sítě** se neodvozuje z forecastového capu: plán nese explicitní `export_limit_w` jako tvrdý limit lokality / invertoru, ne jako tipované maximum z předpovědi.
|
||||
- **ZERO (PASSIVE)** = zero export k CT/zátěži (**142** = `deye_zero_export_mode`); **108/109** jsou typicky **max** z DB. Vynulování jen u **importu bez vybíjení** (`109 = 0` přes `_deye_zero_export_amps_for_passive`). Přetok FVE do sítě při exportu **108 nenuluje** — směr přebytku řeší **142/145** a plán, ne falešné „baterie plná“.
|
||||
- **ZERO (PASSIVE)** = **142** = `deye_zero_export_mode` (1/2, ne selling first). **PV_SURPLUS:** **108 = 0**, **109 = max** — přebytek FVE do sítě (**145 = 1**), ne do baterie. Jinak **108/109** typicky max; výjimka import bez vybíjení → **109 = 0**.
|
||||
- **SELL** = plánovaný export **i** plánované vybíjení (oba záporné) → **142** = selling first, **178** = vypnutý grid peak shaving (32); po návratu do ZERO/CHARGE zase **178** = 48.
|
||||
- Novou logiku vždy ověřit proti **reálnému řádku plánu** (audit / `planning_interval`).
|
||||
|
||||
@@ -51,7 +51,8 @@ Všechny řádky předpokládají **142** = zero export (ne SELL).
|
||||
|
||||
| Situace | Podmínka (plán) | Reg. 108 (max charge A) | Reg. 109 (max discharge A) |
|
||||
|---------|-----------------|-------------------------|----------------------------|
|
||||
| Výchozí | ostatní případy PASSIVE (včetně exportu / přetoku FVE) | max | max |
|
||||
| Přetok FVE do sítě | `export_mode = PV_SURPLUS` (ne SELL) | **0** | max |
|
||||
| Výchozí | ostatní PASSIVE (nabíjení bez exportního záměru) | max | max |
|
||||
| Držet baterii, brát ze sítě | `grid_setpoint_w` > 0 **a** `battery_w` ≤ 0 | max | **0** |
|
||||
|
||||
V obou exportních případech platí, že `export_limit_w` je **tvrdý site/inverter cap**. Nejde o forecastový odhad exportu, takže se může pustit plný přetok v rámci distribučního limitu.
|
||||
|
||||
Reference in New Issue
Block a user