second version
This commit is contained in:
@@ -35,21 +35,23 @@ Samostatná Python služba. Běží jako smyčka, nezávislá na FastAPI.
|
||||
|
||||
Komunikace: Modbus TCP, Unit ID dle DIP přepínače na střídači (typicky 1).
|
||||
|
||||
> Registry jsou specifické pro Deye SUN-20K-SG01LP1-EU.
|
||||
> Finální hodnoty ověřit z Deye Modbus protokolu / Loxone šablony.
|
||||
> Mapování v kódu: `backend/services/telemetry_collector.py` (holding registry, decimal adresa = offset pro `read_holding_registers`).
|
||||
|
||||
| Registr (hex) | Typ | Popis | Jednotka | Přepočet |
|
||||
| Dec (hex) | Typ | Popis | Jednotka | Poznámka |
|
||||
|---|---|---|---|---|
|
||||
| 0x0215 | Read Holding | PV celkový výkon | W | ×1 |
|
||||
| 0x0103 | Read Holding | Battery SoC | % | ×1 |
|
||||
| 0x0105 | Read Holding | Battery power | W | signed, kladné=nabíjení |
|
||||
| 0x0101 | Read Holding | Battery voltage | 0.1V | ×0.1 |
|
||||
| 0x0169 | Read Holding | Grid power | W | signed, kladné=import |
|
||||
| 0x016F | Read Holding | Grid voltage L1 | 0.1V | ×0.1 |
|
||||
| 0x0213 | Read Holding | Load power | W | ×1 |
|
||||
| 0x0220 | Read Holding | Inverter temperature | 0.1°C | ×0.1 |
|
||||
| 0x0168 | Read Holding | Operating mode | enum | viz tabulka módů |
|
||||
| 0x0180 | Read Holding | Fault code | bitfield | 0=ok |
|
||||
| 500 (0x01F4) | uint16 | Provozní stav střídače | enum | raw do `run_state`, ladění |
|
||||
| 514 (0x0202) | uint16 | Dnešní nabití baterie | Wh | `batt_charge_today_wh` |
|
||||
| 515 (0x0203) | uint16 | Dnešní vybití baterie | Wh | `batt_discharge_today_wh` |
|
||||
| 588 (0x024C) | uint16 | Battery SoC | % | `battery_soc_percent` |
|
||||
| 590 (0x024E) | int16 | Tok výkonu baterie | W | signed: **+ vybíjení, − nabíjení** |
|
||||
| 625 (0x0271) | int16 | Výkon sítě | W | signed: **+ import, − export** |
|
||||
| 653 (0x028D) | uint16 | Celková spotřeba | W | `load_power_w` |
|
||||
| 667 (0x029B) | uint16 | Výkon GEN portu (FVE pole B) | W | `gen_port_power_w`, nelze curtailovat |
|
||||
| 672 (0x02A0) | uint16 | Výkon PV1 | W | `pv1_power_w` |
|
||||
| 673 (0x02A1) | uint16 | Výkon PV2 | W | `pv2_power_w` |
|
||||
|
||||
`pv_power_w` v DB = **PV1 + PV2 + GEN port** (celková výroba na instalaci home-01).
|
||||
`gen_port_power_w` zůstává i nadále uložen samostatně pro audit a detailní diagnostiku.
|
||||
|
||||
**Zápis setpointů (plánování → Deye):**
|
||||
|
||||
@@ -60,8 +62,7 @@ Komunikace: Modbus TCP, Unit ID dle DIP přepínače na střídači (typicky 1).
|
||||
| 0x00F6 | Write Single | Grid export power limit | W |
|
||||
| 0x00F0 | Write Single | Work mode | enum (viz tabulka) |
|
||||
|
||||
> **TODO:** Přesné registry doplnit z Deye SUN-20K Modbus protokolu PDF.
|
||||
> Loxone šablona pro Deye je dobrý výchozí bod pro mapování registrů.
|
||||
Rychlá kontrola komunikace: `scripts/test_modbus_deye.py`.
|
||||
|
||||
---
|
||||
|
||||
@@ -108,66 +109,7 @@ Komunikace: Modbus TCP přes Waveshare.
|
||||
|
||||
## Kód telemetrie (Python)
|
||||
|
||||
```python
|
||||
# backend/services/telemetry_collector.py
|
||||
|
||||
import asyncio
|
||||
from pymodbus.client import AsyncModbusTcpClient
|
||||
from datetime import datetime, timezone
|
||||
|
||||
async def poll_inverter(site_id: int, inverter: AssetInverter, endpoint: SiteEndpoint, db):
|
||||
"""Přečte všechny registry Deye a uloží záznam do telemetry_inverter."""
|
||||
async with AsyncModbusTcpClient(endpoint.host, port=endpoint.port) as client:
|
||||
try:
|
||||
# Čtení bloku registrů (optimalizovat jako jeden read multiple)
|
||||
pv_power = await read_register(client, 0x0215, endpoint.unit_id)
|
||||
batt_soc = await read_register(client, 0x0103, endpoint.unit_id)
|
||||
batt_power = await read_register_signed(client, 0x0105, endpoint.unit_id)
|
||||
batt_voltage = await read_register(client, 0x0101, endpoint.unit_id) / 10.0
|
||||
grid_power = await read_register_signed(client, 0x0169, endpoint.unit_id)
|
||||
grid_voltage = await read_register(client, 0x016F, endpoint.unit_id) / 10.0
|
||||
load_power = await read_register(client, 0x0213, endpoint.unit_id)
|
||||
inv_temp = await read_register(client, 0x0220, endpoint.unit_id) / 10.0
|
||||
op_mode = await read_register(client, 0x0168, endpoint.unit_id)
|
||||
fault_code = await read_register(client, 0x0180, endpoint.unit_id)
|
||||
|
||||
await db.execute("""
|
||||
INSERT INTO ems.telemetry_inverter
|
||||
(site_id, inverter_id, measured_at,
|
||||
pv_power_w, battery_soc_percent, battery_power_w, battery_voltage_v,
|
||||
grid_power_w, grid_voltage_v, load_power_w,
|
||||
inverter_temp_c, operating_mode, fault_code)
|
||||
VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13)
|
||||
ON CONFLICT (inverter_id, measured_at) DO NOTHING
|
||||
""",
|
||||
site_id, inverter.id, datetime.now(timezone.utc),
|
||||
pv_power, batt_soc, batt_power, batt_voltage,
|
||||
grid_power, grid_voltage, load_power,
|
||||
inv_temp, str(op_mode), fault_code
|
||||
)
|
||||
|
||||
except Exception as e:
|
||||
logger.warning(f"Inverter poll failed [{inverter.code}]: {e}")
|
||||
raise
|
||||
|
||||
|
||||
async def run_collector(db):
|
||||
"""Hlavní smyčka – každých 60s sbírá data ze všech aktivních zařízení."""
|
||||
while True:
|
||||
start = asyncio.get_event_loop().time()
|
||||
|
||||
sites = await db.fetch("SELECT id FROM ems.site WHERE active = true")
|
||||
for site in sites:
|
||||
await asyncio.gather(
|
||||
poll_all_inverters(site.id, db),
|
||||
poll_all_ev_chargers(site.id, db),
|
||||
poll_all_heat_pumps(site.id, db),
|
||||
return_exceptions=True # jeden výpadek nezastaví ostatní
|
||||
)
|
||||
|
||||
elapsed = asyncio.get_event_loop().time() - start
|
||||
await asyncio.sleep(max(0, 60 - elapsed))
|
||||
```
|
||||
Implementace: `backend/services/telemetry_collector.py` — `poll_inverter()` používá konstanty `DEYE_REG_*` a třídu `ModbusDevice`; hlavní smyčka je `run_telemetry_loop` / `run_telemetry_loop_wrapper`.
|
||||
|
||||
---
|
||||
|
||||
@@ -209,7 +151,7 @@ MODBUS_READ_TIMEOUT_SEC=3
|
||||
|
||||
## Otevřené body
|
||||
|
||||
- [ ] Doplnit přesné Modbus registry Deye z PDF protokolu
|
||||
- [x] Základní mapování Deye (holding registry 500–673) v `telemetry_collector.py`
|
||||
- [ ] Doplnit Modbus registry Teltonika z dokumentace / Loxone šablony
|
||||
- [ ] Doplnit Modbus registry Samsung z dokumentace / Loxone šablony
|
||||
- [ ] Ověřit Unit ID všech zařízení při instalaci
|
||||
|
||||
Reference in New Issue
Block a user