217 lines
8.0 KiB
Markdown
217 lines
8.0 KiB
Markdown
# Modul: Telemetry (Sběr dat ze zařízení)
|
||
|
||
## Co modul dělá
|
||
|
||
- Čte data ze střídače Deye, EV nabíječek Teltonika a tepelného čerpadla Samsung přes Modbus TCP
|
||
- Ukládá surová měření do DB (1min granularita)
|
||
- Detekuje výpadky komunikace a loguje chyby
|
||
- Agreguje 1min data na 15min průměry pro spotřebu, audit a plánování
|
||
|
||
---
|
||
|
||
## Komponenta: `telemetry_collector` (Python service)
|
||
|
||
Samostatná Python služba. Běží jako smyčka, nezávislá na FastAPI.
|
||
|
||
### Polling intervaly
|
||
|
||
| Zařízení | Interval | Důvod |
|
||
|---|---|---|
|
||
| Deye střídač | 60 s | 1min granularita telemetrie |
|
||
| Teltonika EV nabíječka 1 | 60 s | |
|
||
| Teltonika EV nabíječka 2 | 60 s | |
|
||
| Samsung tepelné čerpadlo | 60 s | |
|
||
|
||
### Chování při chybě
|
||
|
||
- Chyba komunikace: záznam se nezapíše, chyba se loguje
|
||
- 3 po sobě jdoucí chyby = alert (log WARNING)
|
||
- 10 po sobě jdoucích chyb = log ERROR + pokus o reconnect
|
||
- Data se neinterpolují – chybějící minuty zůstanou prázdné (audit to pozná)
|
||
|
||
---
|
||
|
||
## Deye SUN-20K – Modbus registry
|
||
|
||
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.
|
||
|
||
| Registr (hex) | Typ | Popis | Jednotka | Přepočet |
|
||
|---|---|---|---|---|
|
||
| 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 |
|
||
|
||
**Zápis setpointů (plánování → Deye):**
|
||
|
||
| Registr (hex) | Typ | Popis | Hodnota |
|
||
|---|---|---|---|
|
||
| 0x00F3 | Write Single | Battery charge power limit | W |
|
||
| 0x00F4 | Write Single | Battery discharge power limit | W |
|
||
| 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ů.
|
||
|
||
---
|
||
|
||
## Teltonika TeltoCharge – Modbus registry
|
||
|
||
Komunikace: Modbus TCP přes Waveshare, Unit ID = 1 (ověřit).
|
||
|
||
> Registry doplnit z Teltonika TeltoCharge Modbus dokumentace / Loxone šablony.
|
||
|
||
| Registr | Typ | Popis | Jednotka |
|
||
|---|---|---|---|
|
||
| TBD | Read | Stav konektoru (OCPP status enum) | enum |
|
||
| TBD | Read | Aktuální výkon | W |
|
||
| TBD | Read | Kumulativní energie session | Wh |
|
||
| TBD | Read | Proud L1/L2/L3 | 0.1A |
|
||
| TBD | Read | Napětí | 0.1V |
|
||
| TBD | Read | Session ID | uint |
|
||
| TBD | Read | Error code | uint |
|
||
| TBD | Write | Max proud (charge limit) | A (6–32A) |
|
||
| TBD | Write | Povolení nabíjení (on/off) | bool |
|
||
|
||
---
|
||
|
||
## Samsung tepelné čerpadlo – Modbus registry
|
||
|
||
Komunikace: Modbus TCP přes Waveshare.
|
||
|
||
> Registry doplnit ze Samsung NASA Modbus dokumentace / Loxone šablony.
|
||
|
||
| Registr | Typ | Popis | Jednotka |
|
||
|---|---|---|---|
|
||
| TBD | Read | Venkovní teplota | 0.1°C |
|
||
| TBD | Read | Teplota vody vstup | 0.1°C |
|
||
| TBD | Read | Teplota vody výstup | 0.1°C |
|
||
| TBD | Read | Teplota zásobníku TUV | 0.1°C |
|
||
| TBD | Read | Příkon | W |
|
||
| TBD | Read | Provozní režim | enum |
|
||
| TBD | Read | Alarm kód | uint |
|
||
| TBD | Read | Odmrazování aktivní | bool |
|
||
| TBD | Write | Povolení provozu | bool |
|
||
| TBD | Write | Požadovaná teplota TUV | °C |
|
||
|
||
---
|
||
|
||
## 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))
|
||
```
|
||
|
||
---
|
||
|
||
## Agregace 1min → 15min
|
||
|
||
Prováděna PostgreSQL funkcí `ems.fn_fill_audit_interval()` a `ems.fn_fill_baseline_consumption()`.
|
||
Spouštěna každých 15 minut jako scheduled task (Python APScheduler nebo pg_cron).
|
||
|
||
```sql
|
||
-- Příklad agregace telemetrie na 15min průměr
|
||
-- (součást fn_fill_audit_interval)
|
||
SELECT
|
||
site_id,
|
||
time_bucket('15 minutes', measured_at) AS interval_start,
|
||
AVG(pv_power_w)::INT AS avg_pv_power_w,
|
||
AVG(battery_power_w)::INT AS avg_battery_power_w,
|
||
AVG(grid_power_w)::INT AS avg_grid_power_w,
|
||
AVG(load_power_w)::INT AS avg_load_power_w,
|
||
LAST(battery_soc_percent, measured_at) AS last_soc_pct
|
||
FROM ems.telemetry_inverter
|
||
WHERE measured_at >= $1 AND measured_at < $1 + INTERVAL '15 minutes'
|
||
AND site_id = $2
|
||
GROUP BY site_id, time_bucket('15 minutes', measured_at);
|
||
```
|
||
|
||
---
|
||
|
||
## Konfigurace (env proměnné)
|
||
|
||
```env
|
||
TELEMETRY_POLL_INTERVAL_SEC=60
|
||
TELEMETRY_ERROR_WARN_THRESHOLD=3 # počet chyb před WARNING logem
|
||
TELEMETRY_ERROR_RECONNECT_THRESHOLD=10
|
||
MODBUS_CONNECT_TIMEOUT_SEC=5
|
||
MODBUS_READ_TIMEOUT_SEC=3
|
||
```
|
||
|
||
---
|
||
|
||
## Otevřené body
|
||
|
||
- [ ] Doplnit přesné Modbus registry Deye z PDF protokolu
|
||
- [ ] 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
|
||
- [ ] Optimalizovat čtení Deye jako jeden `read_holding_registers` blok místo jednotlivých registrů
|