From d8f6de77d5b3e554612484b3a91472a1bede8c1f Mon Sep 17 00:00:00 2001 From: Dusan Vojacek Date: Fri, 12 Jun 2026 20:24:56 +0200 Subject: [PATCH] =?UTF-8?q?home-01:=20ulice=20z=20extern=C3=ADho=20CT=20(r?= =?UTF-8?q?eg=20619)=20+=20celkov=C3=A1=20spot=C5=99eba=20domu;=20Deye=20z?= =?UTF-8?q?ero-export=20to=20CT?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fakturační elektroměr ~8 kW vs Deye 13.5 kW: hlavní okruhy domu (vč. wallboxu, EV 10.5 kW při load 164 W) visí MEZI střídačem a CT u elektroměru — reg 625 (svorky) ani 653 (UPS port) je nevidí. home-01 bylo chybně vedeno jako bez CT. V100: deye_zero_export_mode=2 (reg 142 → zero export to CT, propíše exporter), sloupce inverter_grid_port_w + ups_load_w, komentáře se změnou sémantiky. Collector: grid_power_w z reg 619 (instalace s CT; fallback 625), load_power_w = pv + baterie + grid = celkový dům. R__049 +2 parametry, R__052 + deye_zero_export_mode. Audit/baseline od teď počítají se skutečnou ulicí; historie (do 2026-06-12) nese svorky střídače — přepočet ekonomiky po faktuře. Baseline rebuild doporučen po týdnu nových dat. Co-Authored-By: Claude Opus 4.8 (1M context) --- backend/services/telemetry_collector.py | 19 +++++++++--- db/migration/V100__home01_grid_ct.sql | 29 +++++++++++++++++++ .../R__049_fn_telemetry_inverter_sample.sql | 14 ++++++--- .../R__052_vw_asset_inverter_modbus_poll.sql | 5 ++-- 4 files changed, 57 insertions(+), 10 deletions(-) create mode 100644 db/migration/V100__home01_grid_ct.sql diff --git a/backend/services/telemetry_collector.py b/backend/services/telemetry_collector.py index 5ebd8cf..67d2e3e 100644 --- a/backend/services/telemetry_collector.py +++ b/backend/services/telemetry_collector.py @@ -239,7 +239,8 @@ DEYE_REG_BATT_CHARGE_TODAY = 514 DEYE_REG_BATT_DISCHARGE_TODAY = 515 DEYE_REG_BATTERY_SOC = 588 DEYE_REG_BATTERY_POWER_FLOW = 590 -DEYE_REG_GRID_TOTAL_POWER = 625 +DEYE_REG_GRID_TOTAL_POWER = 625 # tok na grid svorkách střídače +DEYE_REG_GRID_CT_TOTAL_POWER = 619 # tok na externím CT (= ulice; jen instalace s CT) DEYE_REG_GEN_PORT_POWER = 667 DEYE_REG_LOAD_TOTAL_POWER = 653 DEYE_REG_GRID_IMPORT_TOTAL_LO = 522 @@ -342,8 +343,9 @@ async def poll_inverter(site_id: int, db: asyncpg.Connection) -> None: battery_power = await mb.read_register_signed(DEYE_REG_BATTERY_POWER_FLOW) batt_charge_today = await mb.read_register(DEYE_REG_BATT_CHARGE_TODAY) batt_discharge_today = await mb.read_register(DEYE_REG_BATT_DISCHARGE_TODAY) - grid_power = await mb.read_register_signed(DEYE_REG_GRID_TOTAL_POWER) - load_power = await mb.read_register_signed(DEYE_REG_LOAD_TOTAL_POWER) + inverter_grid_port_w = await mb.read_register_signed(DEYE_REG_GRID_TOTAL_POWER) + ups_load_w = await mb.read_register_signed(DEYE_REG_LOAD_TOTAL_POWER) + grid_ct_w = await mb.read_register_signed(DEYE_REG_GRID_CT_TOTAL_POWER) pv1_power = await mb.read_register_signed(DEYE_REG_PV1_POWER) pv2_power = await mb.read_register_signed(DEYE_REG_PV2_POWER) gen_port_power = await mb.read_register_signed(DEYE_REG_GEN_PORT_POWER) @@ -353,6 +355,13 @@ async def poll_inverter(site_id: int, db: asyncpg.Connection) -> None: reg145 = await mb.read_register(DEYE_REG_SOLAR_SELL) reg179 = await mb.read_register(DEYE_REG_CONTROL_BOARD_SPECIAL1) pv_power_w = aggregate_pv_production_w(pv1_power, pv2_power, gen_port_power) + # Ulice: instalace s CT (deye_zero_export_mode=2) čte reg 619; bez CT + # zůstává reg 625. Okruhy MEZI střídačem a CT (home-01: wallbox, + # kuchyň…) jsou vidět jen v CT — reg 625/653 je nezahrnují. + has_ct = int(row["deye_zero_export_mode"] or 1) == 2 + grid_power = grid_ct_w if has_ct else inverter_grid_port_w + # Celková spotřeba domu = pv + baterie(+vybíjí) + grid(+import). + load_power = max(0, pv_power_w + battery_power + grid_power) grid_import_total_wh = (grid_energy_regs[1] << 16 | grid_energy_regs[0]) * 100 grid_export_total_wh = (grid_energy_regs[3] << 16 | grid_energy_regs[2]) * 100 is_export_limited, pv_derating_flags = _export_limit_flags_from_deye_regs(reg145, reg179) @@ -360,7 +369,7 @@ async def poll_inverter(site_id: int, db: asyncpg.Connection) -> None: logger.debug("inverter:%s Deye run_state raw=%s", code, run_state) await db.execute( - "select ems.fn_telemetry_inverter_sample($1::int, $2::int, $3::timestamptz, $4::int, $5::int, $6::int, $7::int, $8::float8, $9::int, $10::int, $11::int, $12::int, $13::int, $14::bigint, $15::bigint, $16::int, $17::boolean, $18::int)", + "select ems.fn_telemetry_inverter_sample($1::int, $2::int, $3::timestamptz, $4::int, $5::int, $6::int, $7::int, $8::float8, $9::int, $10::int, $11::int, $12::int, $13::int, $14::bigint, $15::bigint, $16::int, $17::boolean, $18::int, $19::int, $20::int)", site_id, inv_id, measured_at, @@ -379,6 +388,8 @@ async def poll_inverter(site_id: int, db: asyncpg.Connection) -> None: run_state, is_export_limited, pv_derating_flags, + inverter_grid_port_w, + ups_load_w, ) inv_temp: float | None = None await manager.broadcast_telemetry( diff --git a/db/migration/V100__home01_grid_ct.sql b/db/migration/V100__home01_grid_ct.sql new file mode 100644 index 0000000..de28677 --- /dev/null +++ b/db/migration/V100__home01_grid_ct.sql @@ -0,0 +1,29 @@ +-- home-01 MÁ externí CT u fakturačního elektroměru (ověřeno 2026-06-12: +-- reg 619 = −8.3 kW přesně dle elektroměru, zatímco reg 625 = −13.5 kW na +-- svorkách střídače; hlavní okruhy domu vč. wallboxu visí MEZI střídačem +-- a CT). Dosud vedeno chybně jako „nemá CT" (deye_zero_export_mode = 1). + +-- 1) Deye má regulovat zero-export podle CT (reg 142 = 2); exporter hodnotu +-- čte z tohoto sloupce a propíše ji při nejbližším ticku. +update ems.asset_inverter + set deye_zero_export_mode = 2 + where code = 'deye-main' + and site_id = (select id from ems.site where code = 'home-01'); + +-- 2) Telemetrie: ukládat oba pohledy. grid_power_w nově nese skutečný tok +-- na ulici (reg 619, externí CT); tok na svorkách střídače (reg 625) +-- jde do nového sloupce. load_power_w nově = CELKOVÁ spotřeba domu +-- (pv + baterie − ulice, dopočet v collectoru); zálohovaný load port +-- (reg 653) jde do ups_load_w. +alter table ems.telemetry_inverter + add column if not exists inverter_grid_port_w int, + add column if not exists ups_load_w int; + +comment on column ems.telemetry_inverter.grid_power_w is + 'Tok na ulici dle externího CT (Deye reg 619; +import / −export). Do 2026-06-12 obsahoval tok na svorkách střídače (reg 625) — u instalací s okruhy mezi střídačem a CT (home-01) byl export nadhodnocen.'; +comment on column ems.telemetry_inverter.inverter_grid_port_w is + 'Tok na grid svorkách střídače (Deye reg 625; +import / −export). Rozdíl proti grid_power_w = spotřeba okruhů mezi střídačem a CT.'; +comment on column ems.telemetry_inverter.load_power_w is + 'CELKOVÁ spotřeba domu: pv + baterie − grid_ct (dopočet collectoru od 2026-06-12). Dříve Deye reg 653 (jen zálohovaný load port) — bazál byl podhodnocený o okruhy na straně sítě.'; +comment on column ems.telemetry_inverter.ups_load_w is + 'Zálohovaný load/UPS port střídače (Deye reg 653) — jen vybrané okruhy.'; diff --git a/db/routines/R__049_fn_telemetry_inverter_sample.sql b/db/routines/R__049_fn_telemetry_inverter_sample.sql index ee6162c..7819bda 100644 --- a/db/routines/R__049_fn_telemetry_inverter_sample.sql +++ b/db/routines/R__049_fn_telemetry_inverter_sample.sql @@ -18,7 +18,9 @@ CREATE OR REPLACE FUNCTION ems.fn_telemetry_inverter_sample( p_grid_export_total_wh bigint, p_run_state int, p_is_export_limited boolean DEFAULT NULL, - p_pv_derating_flags int DEFAULT NULL + p_pv_derating_flags int DEFAULT NULL, + p_inverter_grid_port_w int DEFAULT NULL, + p_ups_load_w int DEFAULT NULL ) RETURNS void LANGUAGE sql @@ -41,7 +43,9 @@ AS $fn$ grid_export_total_wh, run_state, is_export_limited, - pv_derating_flags + pv_derating_flags, + inverter_grid_port_w, + ups_load_w ) VALUES ( p_site_id, @@ -61,10 +65,12 @@ AS $fn$ p_grid_export_total_wh, p_run_state, p_is_export_limited, - p_pv_derating_flags + p_pv_derating_flags, + p_inverter_grid_port_w, + p_ups_load_w ) ON CONFLICT (inverter_id, measured_at) DO NOTHING; $fn$; COMMENT ON FUNCTION ems.fn_telemetry_inverter_sample IS - 'Insert jednoho vzorku telemetrie střídače (telemetry_collector). Volitelně is_export_limited / pv_derating_flags (Deye reg 145/179) pro vyloučení slotů z učení PV delty.'; + 'Insert jednoho vzorku telemetrie střídače (telemetry_collector). grid_power_w = ulice (CT reg 619 u instalací s CT, jinak reg 625); load_power_w = celková spotřeba domu (dopočet); inverter_grid_port_w = reg 625; ups_load_w = reg 653.'; diff --git a/db/views/R__052_vw_asset_inverter_modbus_poll.sql b/db/views/R__052_vw_asset_inverter_modbus_poll.sql index b1d95de..706ccc4 100644 --- a/db/views/R__052_vw_asset_inverter_modbus_poll.sql +++ b/db/views/R__052_vw_asset_inverter_modbus_poll.sql @@ -7,7 +7,8 @@ select ai.code, se.host, se.port, - se.unit_id + se.unit_id, + ai.deye_zero_export_mode from ems.asset_inverter ai join ems.site_endpoint se on se.id = ai.endpoint_id where ai.active = true @@ -15,4 +16,4 @@ where ai.active = true and se.endpoint_type = 'modbus_tcp'; comment on view ems.vw_asset_inverter_modbus_poll is - 'Aktivní střídače s Modbus TCP endpointem pro telemetry_collector.'; + 'Aktivní střídače s Modbus TCP endpointem pro telemetry_collector. deye_zero_export_mode 2 = osazené externí CT → tok ulice z reg 619.';