dalsi pokus ladeni #7
@@ -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`.
|
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_zero_export_amps_for_passive`; **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):** **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`**.
|
||||||
|
|
||||||
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).
|
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).
|
||||||
|
|
||||||
|
|||||||
@@ -244,15 +244,14 @@ def _deye_zero_export_amps_for_passive(
|
|||||||
max_discharge_a: int,
|
max_discharge_a: int,
|
||||||
) -> tuple[int, int]:
|
) -> tuple[int, int]:
|
||||||
"""
|
"""
|
||||||
PASSIVE (zero export k CT/zátěži): výchozí plné 108/109.
|
PASSIVE (zero export k CT/zátěži): asymetrie jen tam, kde dává smysl pro import.
|
||||||
|
|
||||||
Export v plánu bez vybíjení baterie vypne charge A; import bez nabíjení vypne discharge A.
|
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í.
|
||||||
"""
|
"""
|
||||||
if grid_w < 0 and bat_w >= 0:
|
|
||||||
return 0, max_discharge_a
|
|
||||||
if grid_w > 0 and bat_w <= 0:
|
if grid_w > 0 and bat_w <= 0:
|
||||||
return max_charge_a, 0
|
return max_charge_a, 0
|
||||||
return max_charge_a, max_discharge_a
|
return int(max_charge_a), int(max_discharge_a)
|
||||||
|
|
||||||
|
|
||||||
def deye_battery_charge_discharge_amps(
|
def deye_battery_charge_discharge_amps(
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ class PassivePvSurplusChargeAmpsTests(unittest.TestCase):
|
|||||||
self.assertGreater(ch, 0)
|
self.assertGreater(ch, 0)
|
||||||
self.assertEqual(dis, 0)
|
self.assertEqual(dis, 0)
|
||||||
|
|
||||||
def test_passive_zero_export_still_zero_charge_when_no_battery_charge(self) -> None:
|
def test_passive_export_without_battery_charge_uses_max_108(self) -> None:
|
||||||
ch, dis = deye_battery_charge_discharge_amps(
|
ch, dis = deye_battery_charge_discharge_amps(
|
||||||
lock_battery=False,
|
lock_battery=False,
|
||||||
deye_mode="PASSIVE",
|
deye_mode="PASSIVE",
|
||||||
@@ -46,7 +46,7 @@ class PassivePvSurplusChargeAmpsTests(unittest.TestCase):
|
|||||||
max_charge_a=100,
|
max_charge_a=100,
|
||||||
max_discharge_a=100,
|
max_discharge_a=100,
|
||||||
)
|
)
|
||||||
self.assertEqual(ch, 0)
|
self.assertEqual(ch, 100)
|
||||||
self.assertEqual(dis, 100)
|
self.assertEqual(dis, 100)
|
||||||
|
|
||||||
def test_sell_unchanged(self) -> None:
|
def test_sell_unchanged(self) -> None:
|
||||||
|
|||||||
@@ -11,12 +11,11 @@ from services.control.exporter_monolith import (
|
|||||||
_deye_reg178_verify_with_double_read,
|
_deye_reg178_verify_with_double_read,
|
||||||
_deye_tou_params,
|
_deye_tou_params,
|
||||||
_deye_tou_power_verify_match,
|
_deye_tou_power_verify_match,
|
||||||
_deye_zero_export_amps_for_passive,
|
|
||||||
deye_reg_triggers_self_sustain_after_verify_exhaust,
|
deye_reg_triggers_self_sustain_after_verify_exhaust,
|
||||||
get_deye_mode,
|
get_deye_mode,
|
||||||
)
|
)
|
||||||
from services.control.models import OperatingModeInfo
|
from services.control.models import OperatingModeInfo
|
||||||
from services.control.setpoints import _build_setpoints
|
from services.control.setpoints import _build_setpoints, _deye_zero_export_amps_for_passive
|
||||||
|
|
||||||
|
|
||||||
def _inv(*, min_soc: int | None = 12, reserve_soc: int | None = 20) -> InverterConfig:
|
def _inv(*, min_soc: int | None = 12, reserve_soc: int | None = 20) -> InverterConfig:
|
||||||
@@ -273,7 +272,7 @@ class DeyeTouParamsTests(unittest.TestCase):
|
|||||||
|
|
||||||
def test_zero_export_amps_fve_overflow(self) -> None:
|
def test_zero_export_amps_fve_overflow(self) -> None:
|
||||||
c, d = _deye_zero_export_amps_for_passive(-1000, 0, 100, 90)
|
c, d = _deye_zero_export_amps_for_passive(-1000, 0, 100, 90)
|
||||||
self.assertEqual(c, 0)
|
self.assertEqual(c, 100)
|
||||||
self.assertEqual(d, 90)
|
self.assertEqual(d, 90)
|
||||||
|
|
||||||
def test_zero_export_amps_import_hold_discharge(self) -> None:
|
def test_zero_export_amps_import_hold_discharge(self) -> None:
|
||||||
|
|||||||
@@ -152,7 +152,7 @@ bits 0–1). Detail registrů: [`modbus-registers.md`](modbus-registers.md) (reg
|
|||||||
| **CHARGE** | `battery_w` > 0 **a** `grid_setpoint_w` > 0 |
|
| **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 (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 exportuje přebytek, platí pass-through: **108 = 0**, **109 = max** (`_deye_zero_export_amps_for_passive`). **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): 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*).
|
||||||
|
|
||||||
**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`.
|
**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`.
|
||||||
|
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ EMS zapisuje řídící hodnoty přes journal (`modbus_command`) a **`write_regi
|
|||||||
| 130 | Grid charge enable | 0/1 | — | 1 = povolit nabíjení ze sítě |
|
| 130 | Grid charge enable | 0/1 | — | 1 = povolit nabíjení ze sítě |
|
||||||
| 141 | Energy mgmt mode | bitmask | — | EMS vždy **0** (neměnit jinak) |
|
| 141 | Energy mgmt mode | bitmask | — | EMS vždy **0** (neměnit jinak) |
|
||||||
| 142 | Limit control (System work mode) | 0/1/2 | — | **0** = selling first, **1** = zero export to load, **2** = zero export to CT. Hodnota v non-SELL režimech pochází z `asset_inverter.deye_zero_export_mode` (závisí na instalaci – viz tabulka níže). V režimu SELL vždy **0**. |
|
| 142 | Limit control (System work mode) | 0/1/2 | — | **0** = selling first, **1** = zero export to load, **2** = zero export to CT. Hodnota v non-SELL režimech pochází z `asset_inverter.deye_zero_export_mode` (závisí na instalaci – viz tabulka níže). V režimu SELL vždy **0**. |
|
||||||
| 145 | Solar sell | 0/1 | — | **0** = disabled (přebytek FVE na **straně měniče** se nesmí vést do sítě — curtailment vůči síti), **1** = enabled. Platí jen pro **FVE pod kontrolou Deye** (`controllable = true`); druhá pole (např. **pv-b** u home-01) EMS tímto registerem neřídí. EMS dnes **vždy zapisuje 1**; při 108 = 0 a 145 = 1 přebytky z řiditelného stringu typicky tečou do sítě (viz pass-through níže). |
|
| 145 | Solar sell | 0/1 | — | **0** = disabled (přebytek FVE na **straně měniče** se nesmí vést do sítě — curtailment vůči síti), **1** = enabled. Platí jen pro **FVE pod kontrolou Deye** (`controllable = true`); druhá pole (např. **pv-b** u home-01) EMS tímto registerem neřídí. EMS dnes **vždy zapisuje 1**; směr přebytku (baterie vs. síť) řeší energie management měniče a **142**, ne umělé **108 = 0** (viz pass-through níže). |
|
||||||
| 340 | Max solar power | 0 … cap (W) | 1 W | Strop výkonu DC PV řízeného střídačem (pole A). EMS zapisuje jen pokud `ems.fn_site_has_active_green_bonus_pv(site_id)` (zelený bonus na PV poli) **a** `ems.fn_inverter_pv_a_max_w(inverter_id) > 0`. Hodnota z aktivního `planning_interval`: bez curtailmentu = cap; s `pv_a_curtailed_w > 0` = `clamp(0, cap, pv_a_forecast_solver_w − pv_a_curtailed_w)`. **Není** v `DEYE_CRITICAL_REGS_SELF_SUSTAIN` — verify mismatch nečeká přepnutí do SELF_SUSTAIN. |
|
| 340 | Max solar power | 0 … cap (W) | 1 W | Strop výkonu DC PV řízeného střídačem (pole A). EMS zapisuje jen pokud `ems.fn_site_has_active_green_bonus_pv(site_id)` (zelený bonus na PV poli) **a** `ems.fn_inverter_pv_a_max_w(inverter_id) > 0`. Hodnota z aktivního `planning_interval`: bez curtailmentu = cap; s `pv_a_curtailed_w > 0` = `clamp(0, cap, pv_a_forecast_solver_w − pv_a_curtailed_w)`. **Není** v `DEYE_CRITICAL_REGS_SELF_SUSTAIN` — verify mismatch nečeká přepnutí do SELF_SUSTAIN. |
|
||||||
| 143 | Export limit W | závisí na typu (SUN-20K až ~13 500) | 1 W | Max export do sítě; hodnota z `site_grid_connection.max_export_power_w`. EMS ji neodvozuje z forecastu ani z `grid_setpoint_w`; pro exportní sloty je to tvrdý site/inverter cap. |
|
| 143 | Export limit W | závisí na typu (SUN-20K až ~13 500) | 1 W | Max export do sítě; hodnota z `site_grid_connection.max_export_power_w`. EMS ji neodvozuje z forecastu ani z `grid_setpoint_w`; pro exportní sloty je to tvrdý site/inverter cap. |
|
||||||
| 178 | Control board special 1 | bitmask | — | Bitové pole pro více funkcí. **EMS používá:** (a) bits **4–5** pro peak shaving switch: **32** (`0b00100000`, bit4–5 = **10**) v režimu **SELL**; **48** (`0b00110000`, bit4–5 = **11**) v **PASSIVE/CHARGE**. (b) **BA81:** bits **0–1** pro „MI export to Grid cutoff“ (AC coupling / GEN): **2** = disable (cutoff OFF), **3** = enable (cutoff ON). EMS zapisuje jako **read-modify-write** (zachová ostatní bity). V některých manuálech/UI je to označené jako „register 179“ (1-based). |
|
| 178 | Control board special 1 | bitmask | — | Bitové pole pro více funkcí. **EMS používá:** (a) bits **4–5** pro peak shaving switch: **32** (`0b00100000`, bit4–5 = **10**) v režimu **SELL**; **48** (`0b00110000`, bit4–5 = **11**) v **PASSIVE/CHARGE**. (b) **BA81:** bits **0–1** pro „MI export to Grid cutoff“ (AC coupling / GEN): **2** = disable (cutoff OFF), **3** = enable (cutoff ON). EMS zapisuje jako **read-modify-write** (zachová ostatní bity). V některých manuálech/UI je to označené jako „register 179“ (1-based). |
|
||||||
@@ -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**.
|
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` — při exportu v plánu bez vybíjení je **108 = 0** (přetok FVE); při importu bez nabíjení je **109 = 0** (držet baterii). Jinak oba max (AUTO). Detail: `operating-modes.md`.
|
**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`.
|
||||||
|
|
||||||
### BA81: GEN port cut-off (reg 178 bits0–1) z plánu
|
### BA81: GEN port cut-off (reg 178 bits0–1) z plánu
|
||||||
|
|
||||||
@@ -108,12 +108,12 @@ Solver předvybírá sloty pro nabíjení a export-vybíjení (`_select_charge_s
|
|||||||
|
|
||||||
**CHARGE:** TOU řádek nese **`max_soc_percent`** z DB (**clamp 10–100**) jako cíl při **grid charge** (spolu s příznakem grid charge v time pointu). **Energy pattern** („load first“ / „battery first“) v UI střídače zůstává v kompetenci instalace — EMS ho přes Modbus nenastavuje.
|
**CHARGE:** TOU řádek nese **`max_soc_percent`** z DB (**clamp 10–100**) jako cíl při **grid charge** (spolu s příznakem grid charge v time pointu). **Energy pattern** („load first“ / „battery first“) v UI střídače zůstává v kompetenci instalace — EMS ho přes Modbus nenastavuje.
|
||||||
|
|
||||||
**Jak funguje pass-through fyzicky:**
|
**Jak funguje pass-through (logicky):**
|
||||||
|
|
||||||
1. Reg 108 = 0 → baterie se fyzicky nemůže nabíjet (Deye ji považuje za „plnou")
|
1. **108 / 109** typicky **max** z invertoru — horní limity, ne příkaz „nabíjej / vybíjej“.
|
||||||
2. Reg 142 = 1/2 → zero export mode (Deye nebude aktivně prodávat z baterie)
|
2. Reg **142** = 1/2 → zero export to load / CT (instalace závislá).
|
||||||
3. Reg 145 = 1 → solar sell enabled: protože baterie je „plná" (108 = 0), PV přebytky tečou do sítě
|
3. Reg **145** = 1 → solar sell enabled; přebytek řiditelné FVE po zátěži a limitech směřuje do sítě podle firmware.
|
||||||
4. Reg 109 = max → pokud spotřeba překročí FVE, baterie může vybíjet (ochrana self-consumption)
|
4. Plán (`battery_w`, `grid_setpoint_w`) a **CHARGE** / **SELL** větev v `deye_battery_charge_discharge_amps` dál určují asymetrie (např. **CHARGE**: 109 = 0).
|
||||||
|
|
||||||
### `deye_zero_export_mode` per inverter
|
### `deye_zero_export_mode` per inverter
|
||||||
|
|
||||||
|
|||||||
@@ -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`).
|
- **Žá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.
|
- **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`), s **plnými proudy 108/109** jen ve výchozím stavu; pro přetok FVE do sítě nebo odběr ze sítě bez vybíjení baterie se jeden z proudů **vynuluje** (`_deye_zero_export_amps_for_passive`).
|
- **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á“.
|
||||||
- **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.
|
- **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`).
|
- Novou logiku vždy ověřit proti **reálnému řádku plánu** (audit / `planning_interval`).
|
||||||
|
|
||||||
@@ -51,8 +51,7 @@ 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) |
|
| Situace | Podmínka (plán) | Reg. 108 (max charge A) | Reg. 109 (max discharge A) |
|
||||||
|---------|-----------------|-------------------------|----------------------------|
|
|---------|-----------------|-------------------------|----------------------------|
|
||||||
| Výchozí | ostatní případy PASSIVE | max | max |
|
| Výchozí | ostatní případy PASSIVE (včetně exportu / přetoku FVE) | max | max |
|
||||||
| Přetok FVE do sítě | `grid_setpoint_w` < 0 **a** `battery_w` ≥ 0 | **0** | max |
|
|
||||||
| Držet baterii, brát ze sítě | `grid_setpoint_w` > 0 **a** `battery_w` ≤ 0 | max | **0** |
|
| 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.
|
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.
|
||||||
@@ -61,7 +60,7 @@ Nabíjení ze sítě s vysokým cílovým SoC v TOU řeší větev **CHARGE** (g
|
|||||||
|
|
||||||
### ZERO a „zakázaný export FVE do sítě“ (jen řiditelná pole)
|
### ZERO a „zakázaný export FVE do sítě“ (jen řiditelná pole)
|
||||||
|
|
||||||
**Reg. 145 (solar sell)** na Deye je přepínač typu **enabled / disabled** pro **přebytek FVE na straně měniče** (počítá se vůči režimu **142** zero export a stavu **108** — viz `modbus-registers.md`, pass-through krok za krokem).
|
**Reg. 145 (solar sell)** na Deye je přepínač typu **enabled / disabled** pro **přebytek FVE na straně měniče** (vůči režimu **142** zero export a interní logice měniče — viz `modbus-registers.md`, pass-through).
|
||||||
|
|
||||||
- **Pouze to, co EMS umí přes Deye Modbus ovlivnit** — typicky **FVE pole řízené střídačem** (`asset_pv_array.controllable = true`, u referenční lokality **home-01** např. string za Deye).
|
- **Pouze to, co EMS umí přes Deye Modbus ovlivnit** — typicky **FVE pole řízené střídačem** (`asset_pv_array.controllable = true`, u referenční lokality **home-01** např. string za Deye).
|
||||||
- **Pole mimo tento kanál** (např. **pv-b** u **home-01**, `controllable = false`, často samostatný ongrid GEN) **reg. 145 neovládá**; jejich výkon jde do plánovače jako `pv_b_forecast_w`, ale curtailment / solar sell logika Deye se jich netýká.
|
- **Pole mimo tento kanál** (např. **pv-b** u **home-01**, `controllable = false`, často samostatný ongrid GEN) **reg. 145 neovládá**; jejich výkon jde do plánovače jako `pv_b_forecast_w`, ale curtailment / solar sell logika Deye se jich netýká.
|
||||||
@@ -75,7 +74,12 @@ Týká se jen výroby, kterou Deye umí ovlivnit; **pv-b / ongrid GEN** u home-0
|
|||||||
|
|
||||||
#### PV1/PV2 vs. GEN port (důležité pro BLOCK_EXPORT)
|
#### PV1/PV2 vs. GEN port (důležité pro BLOCK_EXPORT)
|
||||||
|
|
||||||
- **PV1/PV2 (hlavní stringy na DC vstupu Deye)**: výkon je v režimu zero-export **řiditelný** (střídač umí výrobu stáhnout až k nule, pokud není odběr a baterie už nemůže nabíjet). Při BLOCK_EXPORT tedy dává smysl „zakázat export“ přes **reg 145 = 0** – Deye zamezí přetokům z těchto stringů.\n+- **GEN port (AC coupling / mikroinvertory / ongrid GEN)**: výkon **nelze plynule omezovat**. Pole vyrábí „co dá slunce“ a pokud ho **nespotřebuje dům + EV/TČ + baterie**, přebytek fyzicky teče do sítě.\n+ - U instalací typu **BA81** je proto k dispozici jen **tvrdý cut-off** (reg 179 bits0–1).\n+ - U **malé baterie** (např. BA81 ~6 kW max charge a navíc při vysokém SoC ještě méně) může při plném osvitu často nastat přebytek i při BLOCK_EXPORT – a bez cut-off by šel do sítě.\n+ - Naopak při **malém osvitu / velké spotřebě** jsou „každé watty z GEN“ užitečné (jít do domu/baterie) a cut-off by zbytečně zahodil výrobu.\n+
|
- **PV1/PV2 (hlavní stringy na DC vstupu Deye)**: výkon je v režimu zero-export **řiditelný** (střídač umí výrobu stáhnout až k nule, pokud není odběr a baterie už nemůže nabíjet). Při BLOCK_EXPORT tedy dává smysl „zakázat export“ přes **reg 145 = 0** – Deye zamezí přetokům z těchto stringů.
|
||||||
|
- **GEN port (AC coupling / mikroinvertory / ongrid GEN)**: výkon **nelze plynule omezovat**. Pole vyrábí „co dá slunce“ a pokud ho **nespotřebuje dům + EV/TČ + baterie**, přebytek fyzicky teče do sítě.
|
||||||
|
- U instalací typu **BA81** je proto k dispozici jen **tvrdý cut-off** (reg 179 bits0–1).
|
||||||
|
- U **malé baterie** (např. BA81 ~6 kW max charge a navíc při vysokém SoC ještě méně) může při plném osvitu často nastat přebytek i při BLOCK_EXPORT – a bez cut-off by šel do sítě.
|
||||||
|
- Naopak při **malém osvitu / velké spotřebě** jsou „každé watty z GEN“ užitečné (jít do domu/baterie) a cut-off by zbytečně zahodil výrobu.
|
||||||
|
|
||||||
Z toho plyne: **cut-off GEN portu je smysluplné řídit podle očekávaného přebytku**, ne jen podle „sell < 0“. Detail návrhu implementace je v `docs/04-modules/planning.md` (sekce o GEN portu a export banu).
|
Z toho plyne: **cut-off GEN portu je smysluplné řídit podle očekávaného přebytku**, ne jen podle „sell < 0“. Detail návrhu implementace je v `docs/04-modules/planning.md` (sekce o GEN portu a export banu).
|
||||||
|
|
||||||
**SELF_SUSTAIN:** `battery_w = None` ⇒ v `get_deye_mode` jako 0 ⇒ **PASSIVE**; v `write_inverter_setpoints` při `self_sustain_local_use=True` → **108 i 109 = max** (bez variant ZERO výše), reg. **142** dle DB, TOU SOC = **`min_soc_percent`**. **PRESERVE:** `lock_battery` → **108 = 0**, **109 = 0**.
|
**SELF_SUSTAIN:** `battery_w = None` ⇒ v `get_deye_mode` jako 0 ⇒ **PASSIVE**; v `write_inverter_setpoints` při `self_sustain_local_use=True` → **108 i 109 = max** (bez variant ZERO výše), reg. **142** dle DB, TOU SOC = **`min_soc_percent`**. **PRESERVE:** `lock_battery` → **108 = 0**, **109 = 0**.
|
||||||
|
|||||||
@@ -73,7 +73,7 @@ Jak to je v implementaci:
|
|||||||
- `export_limit_w = hard cap`
|
- `export_limit_w = hard cap`
|
||||||
- `solar_sell = 1`
|
- `solar_sell = 1`
|
||||||
- `deye_physical_mode = PASSIVE`
|
- `deye_physical_mode = PASSIVE`
|
||||||
- v PASSIVE se pro exportní slot typicky použije `108 = 0`, `109 = max`
|
- v PASSIVE se pro exportní slot (bez plánovaného nabíjení z baterie) používají typicky **`108` i `109` na max** z invertoru; přebytek do sítě řeší **142/145** a firmware, ne umělé **108 = 0** (to dřív matlo měnič jako „baterie plná“)
|
||||||
|
|
||||||
Poznámka:
|
Poznámka:
|
||||||
|
|
||||||
@@ -157,7 +157,7 @@ Jak to je v implementaci:
|
|||||||
- `deye_physical_mode = SELL`
|
- `deye_physical_mode = SELL`
|
||||||
- `reg 142 = 0`
|
- `reg 142 = 0`
|
||||||
- `reg 178 = 32`
|
- `reg 178 = 32`
|
||||||
- `reg 109` na max, `reg 108 = 0`
|
- `reg 109` na max, `reg 108 = 0` (jen ve fyzickém **SELL** — aktivní výdej baterie do sítě; u **PASSIVE** + přetoku FVE už **108** typicky **max**)
|
||||||
|
|
||||||
## 4. Další režimy, které v praxi existují
|
## 4. Další režimy, které v praxi existují
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user