home-01: ulice z externího CT (reg 619) + celková spotřeba domu; Deye zero-export to CT
All checks were successful
CI and deploy / migration-check (push) Successful in 19s
CI and deploy / deploy (push) Successful in 1m5s

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) <noreply@anthropic.com>
This commit is contained in:
Dusan Vojacek
2026-06-12 20:24:56 +02:00
parent 5530253662
commit d8f6de77d5
4 changed files with 57 additions and 10 deletions

View File

@@ -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(

View File

@@ -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.';

View File

@@ -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.';

View File

@@ -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.';