tune microcycling
This commit is contained in:
@@ -109,6 +109,30 @@ def apply_overrides(plan, overrides) -> Setpoints:
|
||||
|
||||
## Zápis do Deye (Modbus)
|
||||
|
||||
### Fyzický režim (`get_deye_mode`)
|
||||
|
||||
Solver rozlišuje **čtyři typy slotů**: **Charge**, **Pass-through**, **Discharge-export**, **Self-consumption**. Na úrovni Deye se mapují na tři fyzické režimy:
|
||||
|
||||
| Fyzický režim | Podmínka z `ControlSetpoints` |
|
||||
|---|---|
|
||||
| **SELL** | `battery_w` < −500 **a** `grid_setpoint_w` < −200 (záměrné vybíjení baterie do sítě) |
|
||||
| **CHARGE** | `battery_w` > 500 **a** `grid_setpoint_w` > 200 (nabíjení ze sítě) |
|
||||
| **PASSIVE** | vše ostatní (pass-through, self-consumption, SELF_SUSTAIN) |
|
||||
|
||||
**Pass-through** (PV → síť, baterie idle) zůstává **PASSIVE** — fyzicky se realizuje nastavením reg 108 = 0 (zákaz nabíjení) + reg 145 = 1 (solar sell), takže PV přebytky tečou do sítě.
|
||||
|
||||
### Klíčové registry podle typu slotu
|
||||
|
||||
| Registr | Charge | Pass-through | Discharge-export | Self-consumption |
|
||||
|---|---|---|---|---|
|
||||
| **108** (charge A) | max z DB | **0** | 0 | **0** |
|
||||
| **109** (discharge A) | max | max | max | max |
|
||||
| **142** (limit control) | `deye_zero_export_mode` | `deye_zero_export_mode` | **0** (selling first) | `deye_zero_export_mode` |
|
||||
| **145** (solar sell) | 1 | 1 | 1 | 1 |
|
||||
| **178** (peak shaving) | 48 | 48 | **32** | 48 |
|
||||
|
||||
Hodnota `deye_zero_export_mode` (1 = zero export to load, 2 = zero export to CT) pochází z `asset_inverter.deye_zero_export_mode` a závisí na fyzické instalaci (přítomnost CT). Detail v [`modbus-registers.md`](modbus-registers.md).
|
||||
|
||||
**TOU (time points, reg. 166+):** SOC závisí na fyzickém režimu z `get_deye_mode` — **SELL** zapisuje ekonomickou rezervu (`reserve_soc_percent`), **PASSIVE** a neaktivní řádky **3–6** provozní minimum (`min_soc_percent`). Viz [`modbus-registers.md`](modbus-registers.md).
|
||||
|
||||
```python
|
||||
|
||||
@@ -17,13 +17,14 @@ EMS zapisuje řídící hodnoty přes journal (`modbus_command`) a **`write_regi
|
||||
| 128 | Grid charge current | 0 … **max dle modelu** (manuál Deye) | 1 A | 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) |
|
||||
| 142 | Limit control | 0/1/2 | — | **0** = selling first, **1** = zero export (built-in CT); EMS přepíná export vs. idle/nabíjení |
|
||||
| 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 (omezí FVE aby neexportoval), **1** = enabled. EMS vždy zapisuje **1**. Při reg 108 = 0 (baterie se nenabíjí) a solar sell = 1 přebytky FVE tečou do sítě. |
|
||||
| 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` |
|
||||
| 178 | Grid peak shaving switch | bitmask | — | EMS zapisuje **pevnou** hodnotu (bez read-modify-write kvůli kolizím s paralelním čtením z Loxone): **32** (`0b00100000`, bit4–5 = **10**) v režimu **SELL**; **48** (`0b00110000`, bit4–5 = **11**) v **PASSIVE** a **CHARGE**. |
|
||||
| 190 | GEN peak shaving | 0–16000 | 1 W | Peak shaving na GEN portu |
|
||||
| 191 | Grid peak shaving power | 0–16000 | 1 W | **EMS NEZAPISUJE** – nastavit **manuálně v SolarmanApp**. Hodnota určuje výkon peak shavingu v **W**. |
|
||||
|
||||
`control_exporter.write_inverter_setpoints` zapisuje přes **`modbus_command`** (journal; jeden řádek na registr) a **`execute_modbus_commands`** odesílá **souvislé bloky jedním FC 0x10** (např. 62–64, 148–159, 166–177, 108–109, 141–142 podle toho, co je ve frontě). Pořadí v journalu: **62–64** (čas, viz níže), **time points 148–177** (jen řádky zařazené do daného běhu), **108, 109, 141, 142, 178, 143**. Popisné názvy v DB bere `DEYE_REGISTER_NAMES`. **Reg 191** EMS nezapisuje.
|
||||
`control_exporter.write_inverter_setpoints` zapisuje přes **`modbus_command`** (journal; jeden řádek na registr) a **`execute_modbus_commands`** odesílá **souvislé bloky jedním FC 0x10** (např. 62–64, 148–159, 166–177, 108–109, 141–143, 145 podle toho, co je ve frontě). Pořadí v journalu: **62–64** (čas, viz níže), **time points 148–177** (jen řádky zařazené do daného běhu), **108, 109, 141, 142, 143, 145, 178**. Popisné názvy v DB bere `DEYE_REGISTER_NAMES`. **Reg 191** EMS nezapisuje.
|
||||
|
||||
### Reg 191 (výkon grid peak shaving)
|
||||
|
||||
@@ -41,18 +42,7 @@ EMS **nezapisuje** read-modify-write (paralelní čtení jinými klienty může
|
||||
|
||||
## Klíčové registry podle fyzického režimu Deye
|
||||
|
||||
Provozní režimy EMS (AUTO, SELF_SUSTAIN, SELL, …) se mapují na **tři fyzické režimy** střídače: **PASSIVE**, **SELL**, **CHARGE**. Ostatní je politika solveru / EMS, ne samostatný „režim“ invertoru.
|
||||
|
||||
| Reg | PASSIVE | SELL | CHARGE |
|
||||
|-----|---------|------|--------|
|
||||
| 142 | 1 (zero export to load) | 0 (selling first) | 1 |
|
||||
| 108 | `max_charge_a` z DB | `max_charge_a` z DB | `battery_watts_to_amps(battery_w, max_charge_a)` |
|
||||
| 109 | `max_discharge_a` z DB | `max_discharge_a` z DB | 0 |
|
||||
| 178 | 48 | 32 | 48 |
|
||||
| 143 | max export W z DB | max export W z DB | max export W z DB |
|
||||
| 141 | 0 | 0 | 0 |
|
||||
|
||||
**Důležité:** V **PASSIVE** i **SELL** jsou registry **108** a **109** vždy na **plném limitu z DB**. Deye si tok energie reguluje sám; snížení 108/109 pod maximum brání reakci na nepředvídatelnou spotřebu nebo přebytky FVE.
|
||||
Provozní režimy EMS (AUTO, SELF_SUSTAIN, SELL, …) se mapují na **tři fyzické režimy** střídače: **PASSIVE**, **SELL**, **CHARGE**. Solver navíc rozlišuje **čtyři typy slotů** – každý typ určuje specifickou kombinaci registrů.
|
||||
|
||||
### Detekce fyzického režimu (`get_deye_mode` v `control_exporter.py`)
|
||||
|
||||
@@ -60,12 +50,50 @@ Vychází z **`grid_setpoint_w`** a **`battery_w`** z `ControlSetpoints` (aktivn
|
||||
|
||||
| Režim | Podmínka |
|
||||
|-------|----------|
|
||||
| **SELL** | `grid_setpoint_w` < −200 |
|
||||
| **SELL** | `battery_w` < −500 **a** `grid_setpoint_w` < −200 (aktivní vybíjení baterie pro export) |
|
||||
| **CHARGE** | `battery_w` > 500 **a** `grid_setpoint_w` > 200 |
|
||||
| **PASSIVE** | vše ostatní (včetně SELF_SUSTAIN, IDLE, …) |
|
||||
| **PASSIVE** | vše ostatní (včetně pass-through, self-consumption, SELF_SUSTAIN, IDLE, …) |
|
||||
|
||||
Režim **CHARGE_CHEAP** v EMS nastaví `grid_setpoint_w` tak, aby platila podmínka importu (> 200 W), jinak by fyzicky zůstal PASSIVE.
|
||||
|
||||
**Důležité:** SELL se aktivuje **pouze** při záměrném vybíjení baterie do sítě (`bat_w < −500`). Pass-through (PV → síť, baterie idle) zůstává v PASSIVE s reg 108 = 0.
|
||||
|
||||
### Čtyři typy slotů a mapování na registry
|
||||
|
||||
Solver předvybírá sloty pro nabíjení a export-vybíjení (`_select_charge_slots`, `_select_discharge_export_slots`). Výsledné setpointy pak určují typ slotu:
|
||||
|
||||
| | **Charge** | **Pass-through** | **Discharge-export** | **Self-consumption** |
|
||||
|---|---|---|---|---|
|
||||
| **Kdy** | Solver: `bat_w > 0` | Solver: `bat_w == 0`, PV > spotřeba | Solver: `bat_w < −500`, `grid_w < −200` | Noc / PV < spotřeba |
|
||||
| **Deye mode** | PASSIVE | PASSIVE | SELL | PASSIVE |
|
||||
| **108** charge A | **max** (z DB) | **0** | 0 | **0** |
|
||||
| **109** discharge A | max | **max** | **max** | **max** |
|
||||
| **142** limit control | `deye_zero_export_mode` (1 nebo 2) | `deye_zero_export_mode` (1 nebo 2) | **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 |
|
||||
| **141** energy mode | 0 | 0 | 0 | 0 |
|
||||
| **TOU SOC** | min_soc_pct | min_soc_pct | reserve_soc_pct | min_soc_pct |
|
||||
|
||||
**Jak funguje pass-through fyzicky:**
|
||||
|
||||
1. Reg 108 = 0 → baterie se fyzicky nemůže nabíjet (Deye ji považuje za „plnou")
|
||||
2. Reg 142 = 1/2 → zero export mode (Deye nebude aktivně prodávat z baterie)
|
||||
3. Reg 145 = 1 → solar sell enabled: protože baterie je „plná" (108 = 0), PV přebytky tečou do sítě
|
||||
4. Reg 109 = max → pokud spotřeba překročí FVE, baterie může vybíjet (ochrana self-consumption)
|
||||
|
||||
### `deye_zero_export_mode` per inverter
|
||||
|
||||
Hodnota registru 142 v non-SELL režimech závisí na fyzické instalaci. Uložena v `asset_inverter.deye_zero_export_mode`:
|
||||
|
||||
| Site | Inverter ID | `deye_zero_export_mode` | Důvod |
|
||||
|---|---|---|---|
|
||||
| home-01 (id=2) | 3 | **1** (zero export to load) | Nemá CT |
|
||||
| BA81 (id=3) | 5 | **2** (zero export to CT) | CT osazeno |
|
||||
| KV1 (id=4) | 7 | **2** (zero export to CT) | CT osazeno |
|
||||
|
||||
**Varování:** Záměna způsobí chybné měření – pokud site nemá CT a nastaví se „to CT" (2), střídač nevidí skutečný odběr. Naopak pokud má CT ale nastaví se „to load" (1), zátěže mimo load port (např. wallbox) nebudou vidět.
|
||||
|
||||
Limity `max_charge_a` / `max_discharge_a` (odvozené z W a BMS) a volitelné stropy **`deye_register_max_charge_a` / `deye_register_max_discharge_a`** pocházejí z DB (`_load_inverter_config`, migrace **V044**). `max_export_power_w` / reg 143 také z DB.
|
||||
|
||||
## Time Points – řízení podle fyzického režimu
|
||||
@@ -80,7 +108,7 @@ Deye má 6 časových bloků. EMS přepisuje **bloky 1–2** (TOU index 0–1) p
|
||||
| 2 | **`next_slot_hhmm()`** – začátek **následujícího** 15min slotu | `planning_interval` pro **další** slot (`_fetch_plan_row_for_slot_offset(..., 1)`) | Přechod na další čtvrthodinu | viz tabulka níže | viz tabulka níže |
|
||||
| 3–6 | **23:55** (2355) | — | Neaktivní (pasivní profil); ne 23:59 — firmware Deye často 2359 neuloží → verify mismatch | **`min_soc_percent`** (DB) | NE |
|
||||
|
||||
**Registry 108 / 109 / 142 / 178 / 143** odpovídají **aktuálnímu** plánu (okamžitý výstup; `setpoints_now` v `write_inverter_setpoints`). TOU řádky 1–2 doplňují stejnou logiku pro časové segmenty (`_deye_tou_params`).
|
||||
**Registry 108 / 109 / 141 / 142 / 143 / 145 / 178** odpovídají **aktuálnímu** plánu (okamžitý výstup; `setpoints_now` v `write_inverter_setpoints`). TOU řádky 1–2 doplňují stejnou logiku pro časové segmenty (`_deye_tou_params`).
|
||||
|
||||
Příklad v 14:18: blok 1 má čas **1415**, blok 2 čas **1430** – mezi 14:15 a 14:29 je aktivní segment z bloku 1 (sladěný s plánem pro 14:15–14:30), po 14:30 blok 2 (plán 14:30–14:45). Po dalším exportu se oba časy posunou (např. 14:30 / 14:45).
|
||||
|
||||
@@ -161,6 +189,7 @@ async def check():
|
||||
|
||||
for name, reg in [
|
||||
('Limit control', 142),
|
||||
('Solar sell', 145),
|
||||
('Peak sw (bit4-5)', 178),
|
||||
('Export limit', 143),
|
||||
('Discharge A', 109),
|
||||
@@ -181,7 +210,7 @@ docker compose exec db psql -U ems_user -d ems -c "
|
||||
SELECT register_name, value_to_write, status,
|
||||
created_at AT TIME ZONE 'Europe/Prague' AS cas
|
||||
FROM ems.modbus_command
|
||||
WHERE site_id=2 AND register IN (108, 109, 142)
|
||||
WHERE site_id=2 AND register IN (108, 109, 142, 145)
|
||||
ORDER BY created_at DESC LIMIT 9;"
|
||||
```
|
||||
|
||||
|
||||
Reference in New Issue
Block a user