second version
This commit is contained in:
@@ -30,7 +30,7 @@ VALUES (
|
||||
|
||||
-- Deye střídač přes Waveshare RS485→TCP
|
||||
INSERT INTO ems.site_endpoint (site_id, endpoint_type, host, port, protocol, unit_id, enabled, notes)
|
||||
SELECT id, 'modbus_tcp', '192.168.1.100', 502, 'modbus_tcp', 1, true, 'Waveshare WS-ETH pro Deye SUN-20K. Unit ID dle DIP přepínače.'
|
||||
SELECT id, 'modbus_tcp', '172.16.1.10', 502, 'modbus_tcp', 1, true, 'Waveshare WS-ETH pro Deye SUN-20K. Unit ID dle DIP přepínače.'
|
||||
FROM ems.site WHERE code = 'home-01';
|
||||
|
||||
-- Teltonika EV nabíječka 1 přes Waveshare
|
||||
@@ -88,7 +88,7 @@ INSERT INTO ems.asset_inverter (site_id, code, manufacturer, model, endpoint_id,
|
||||
SELECT
|
||||
s.id, 'deye-main', 'Deye', 'SUN-20K-SG01LP1-EU',
|
||||
ep.id,
|
||||
20000, 20000, 20000,
|
||||
18000, 18000, 18000,
|
||||
true,
|
||||
'Hlavní hybridní střídač 20kW LV. RS485 Modbus RTU přes Waveshare.'
|
||||
FROM ems.site s
|
||||
@@ -129,8 +129,8 @@ SELECT
|
||||
s.id, inv.id, 'pv-a', 'FVE pole A',
|
||||
10000, -- 10 kWp
|
||||
184,
|
||||
35, -- sklon odhad; upřesnit dle střechy
|
||||
NULL,
|
||||
22, -- sklon odhad; upřesnit dle střechy
|
||||
18,
|
||||
1.0,
|
||||
true,
|
||||
'Hlavní FVE pole řízené Deye střídačem.'
|
||||
|
||||
@@ -41,11 +41,7 @@ COMMENT ON COLUMN ems.site_market_config.green_bonus_asset_code IS
|
||||
'Kód FVE pole (asset_pv_array.code) na které se zelený bonus vztahuje. '
|
||||
'Příklad: pv-b. NULL = bonus se nevztahuje na žádné konkrétní pole.';
|
||||
|
||||
-- Seed: doplnit zelený bonus pro home-01
|
||||
-- (hodnota bonusu bude upřesněna dle smlouvy s OTE/ERU)
|
||||
UPDATE ems.site_market_config
|
||||
SET
|
||||
green_bonus_czk_kwh = 1.20, -- TODO: doplnit skutečnou výši bonusu ze smlouvy
|
||||
green_bonus_asset_code = 'pv-b'
|
||||
WHERE site_id = (SELECT id FROM ems.site WHERE code = 'home-01')
|
||||
AND valid_to IS NULL;
|
||||
-- Seed zeleného bonusu přesunut: V017__green_bonus.sql (ems.asset_pv_array.green_bonus_*).
|
||||
-- Sloupce green_bonus_* na site_market_config odstraňuje V018__cleanup_legacy_green_bonus.sql;
|
||||
-- UPDATE zde by při změně pořadí / rebuild konfliktních migrací selhal.
|
||||
-- UPDATE ems.site_market_config SET green_bonus_czk_kwh = 1.20, green_bonus_asset_code = 'pv-b' ...
|
||||
|
||||
29
db/migration/V012__telemetry_inverter_columns.sql
Normal file
29
db/migration/V012__telemetry_inverter_columns.sql
Normal file
@@ -0,0 +1,29 @@
|
||||
-- Nové sloupce telemetrie Deye (GEN port, PV1/PV2, denní energie baterie, run_state).
|
||||
-- V011 je již použito pro indexy/agregace.
|
||||
|
||||
ALTER TABLE ems.telemetry_inverter
|
||||
ADD COLUMN IF NOT EXISTS pv1_power_w INT,
|
||||
ADD COLUMN IF NOT EXISTS pv2_power_w INT,
|
||||
ADD COLUMN IF NOT EXISTS gen_port_power_w INT,
|
||||
ADD COLUMN IF NOT EXISTS batt_charge_today_wh INT,
|
||||
ADD COLUMN IF NOT EXISTS batt_discharge_today_wh INT,
|
||||
ADD COLUMN IF NOT EXISTS run_state INT;
|
||||
|
||||
COMMENT ON COLUMN ems.telemetry_inverter.pv1_power_w IS
|
||||
'Výkon PV1 vstupu W (Deye holding register).';
|
||||
COMMENT ON COLUMN ems.telemetry_inverter.pv2_power_w IS
|
||||
'Výkon PV2 vstupu W (Deye holding register).';
|
||||
COMMENT ON COLUMN ems.telemetry_inverter.gen_port_power_w IS
|
||||
'Výkon GEN portu W – výroba FVE pole B (ongridový střídač).
|
||||
Nelze řídit, jen měřit. Klíčový pro audit zeleného bonusu.';
|
||||
COMMENT ON COLUMN ems.telemetry_inverter.batt_charge_today_wh IS
|
||||
'Dnešní nabití baterie Wh (denní čítač z Modbus).';
|
||||
COMMENT ON COLUMN ems.telemetry_inverter.batt_discharge_today_wh IS
|
||||
'Dnešní vybití baterie Wh (denní čítač z Modbus).';
|
||||
COMMENT ON COLUMN ems.telemetry_inverter.run_state IS
|
||||
'Provozní stav střídače (raw enum z Modbus registru run_state).';
|
||||
|
||||
COMMENT ON COLUMN ems.telemetry_inverter.battery_power_w IS
|
||||
'Výkon baterie v W (signed). Kladné = vybíjení, záporné = nabíjení (mapování dle registru 590).';
|
||||
COMMENT ON COLUMN ems.telemetry_inverter.pv_power_w IS
|
||||
'Součet okamžitého výkonu FVE stringů na střídači (typicky PV1+PV2) v W.';
|
||||
20
db/migration/V013__predicted_negative_price_window.sql
Normal file
20
db/migration/V013__predicted_negative_price_window.sql
Normal file
@@ -0,0 +1,20 @@
|
||||
-- Predikovaná okna záporných spotových cen (cache pro UI / API)
|
||||
|
||||
CREATE TABLE ems.predicted_negative_price_window (
|
||||
id SERIAL PRIMARY KEY,
|
||||
site_id INT NOT NULL REFERENCES ems.site (id),
|
||||
predicted_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
||||
predicted_date DATE NOT NULL,
|
||||
window_start_hour INT NOT NULL,
|
||||
window_end_hour INT NOT NULL,
|
||||
probability_pct INT NOT NULL,
|
||||
expected_min_price NUMERIC(10, 4),
|
||||
reason TEXT,
|
||||
UNIQUE (site_id, predicted_date, window_start_hour)
|
||||
);
|
||||
|
||||
COMMENT ON TABLE ems.predicted_negative_price_window IS
|
||||
'Výstup ems.fn_predict_negative_price_windows – predikce intervalů s rizikem záporné nákupní ceny; obnovovat po importu cen a forecastu FVE.';
|
||||
|
||||
CREATE INDEX idx_predicted_neg_price_site_date
|
||||
ON ems.predicted_negative_price_window (site_id, predicted_date);
|
||||
78
db/migration/V014__asset_model_refinement.sql
Normal file
78
db/migration/V014__asset_model_refinement.sql
Normal file
@@ -0,0 +1,78 @@
|
||||
-- =============================================================
|
||||
-- V014__asset_model_refinement.sql
|
||||
-- Rozlišení limitů: AC střídač / DC FVE / baterie přes měnič / BMS+C-rate
|
||||
-- =============================================================
|
||||
|
||||
-- Střídač: nové sloupce (staré max_charge_power_w / max_discharge_power_w ponechány kvůli kompatibilitě)
|
||||
ALTER TABLE ems.asset_inverter
|
||||
ADD COLUMN IF NOT EXISTS max_ac_output_w INT,
|
||||
ADD COLUMN IF NOT EXISTS max_dc_input_w INT,
|
||||
ADD COLUMN IF NOT EXISTS max_battery_charge_w INT,
|
||||
ADD COLUMN IF NOT EXISTS max_battery_discharge_w INT,
|
||||
ADD COLUMN IF NOT EXISTS gen_port_max_power_w INT;
|
||||
|
||||
COMMENT ON COLUMN ems.asset_inverter.max_ac_output_w IS
|
||||
'Maximální AC výkon střídače v W. Deye SUN-20K = 22000 W.';
|
||||
COMMENT ON COLUMN ems.asset_inverter.max_dc_input_w IS
|
||||
'Maximální DC vstupní výkon z FVE v W. Deye SUN-20K = 40000 W.';
|
||||
COMMENT ON COLUMN ems.asset_inverter.max_battery_charge_w IS
|
||||
'Maximální výkon nabíjení baterie v W – limit střídače (ne BMS).
|
||||
Deye SUN-20K s LV baterií = 18000 W (350A × 51.2V).';
|
||||
COMMENT ON COLUMN ems.asset_inverter.max_battery_discharge_w IS
|
||||
'Maximální výkon vybíjení baterie v W – limit střídače.
|
||||
Deye SUN-20K s LV baterií = 18000 W.';
|
||||
COMMENT ON COLUMN ems.asset_inverter.gen_port_max_power_w IS
|
||||
'Maximální výkon GEN portu v W. Zahrnuje součet všech zařízení
|
||||
zapojených do GEN portu (mikroinvertory, ongrid střídač).
|
||||
Pro home-01 = 10080 W (pv-b pole).
|
||||
Pro druhou instalaci = 4400 W (2× 2.2 kW mikroinvertory).';
|
||||
|
||||
-- Baterie: C-rate a BMS
|
||||
ALTER TABLE ems.asset_battery
|
||||
ADD COLUMN IF NOT EXISTS max_charge_c_rate NUMERIC(4,2),
|
||||
ADD COLUMN IF NOT EXISTS max_discharge_c_rate NUMERIC(4,2),
|
||||
ADD COLUMN IF NOT EXISTS bms_max_charge_w INT,
|
||||
ADD COLUMN IF NOT EXISTS bms_max_discharge_w INT;
|
||||
|
||||
COMMENT ON COLUMN ems.asset_battery.max_charge_c_rate IS
|
||||
'Maximální nabíjecí C-rate. 0.5C pro 64 kWh = 32 kW teoretické maximum.
|
||||
Skutečný limit je min(bms_max_charge_w, inverter.max_battery_charge_w).';
|
||||
COMMENT ON COLUMN ems.asset_battery.max_discharge_c_rate IS
|
||||
'Maximální vybíjecí C-rate (symetricky k nabíjení).';
|
||||
COMMENT ON COLUMN ems.asset_battery.bms_max_charge_w IS
|
||||
'Maximální nabíjecí výkon dle BMS v W. Pokud NULL, použij C-rate výpočet.';
|
||||
COMMENT ON COLUMN ems.asset_battery.bms_max_discharge_w IS
|
||||
'Maximální vybíjecí výkon dle BMS v W. Pokud NULL, použij C-rate výpočet.';
|
||||
|
||||
-- Z existujících sloupců přejmenujeme sémantiku do nových (kde ještě nejsou vyplněné)
|
||||
UPDATE ems.asset_inverter
|
||||
SET
|
||||
max_battery_charge_w = COALESCE(max_battery_charge_w, max_charge_power_w),
|
||||
max_battery_discharge_w = COALESCE(max_battery_discharge_w, max_discharge_power_w)
|
||||
WHERE max_charge_power_w IS NOT NULL
|
||||
OR max_discharge_power_w IS NOT NULL;
|
||||
|
||||
-- Seed home-01: hlavní Deye (ne ongrid řádek)
|
||||
UPDATE ems.asset_inverter inv
|
||||
SET
|
||||
max_ac_output_w = 22000,
|
||||
max_dc_input_w = 40000,
|
||||
max_battery_charge_w = 18000,
|
||||
max_battery_discharge_w = 18000,
|
||||
gen_port_max_power_w = 10080
|
||||
FROM ems.site s
|
||||
WHERE s.id = inv.site_id
|
||||
AND s.code = 'home-01'
|
||||
AND inv.code = 'deye-main';
|
||||
|
||||
UPDATE ems.asset_battery ab
|
||||
SET
|
||||
max_charge_c_rate = 0.28,
|
||||
max_discharge_c_rate = 0.28,
|
||||
bms_max_charge_w = 18000,
|
||||
bms_max_discharge_w = 18000
|
||||
FROM ems.asset_inverter inv
|
||||
JOIN ems.site s ON s.id = inv.site_id
|
||||
WHERE ab.inverter_id = inv.id
|
||||
AND s.code = 'home-01'
|
||||
AND inv.code = 'deye-main';
|
||||
100
db/migration/V015__distribution_tariff.sql
Normal file
100
db/migration/V015__distribution_tariff.sql
Normal file
@@ -0,0 +1,100 @@
|
||||
-- ============================================================
|
||||
-- Distribuční tarify a HDO
|
||||
-- ============================================================
|
||||
|
||||
CREATE TABLE ems.distribution_tariff (
|
||||
id SERIAL PRIMARY KEY,
|
||||
distributor TEXT NOT NULL, -- 'EGD', 'CEZ', 'PRE'
|
||||
code TEXT NOT NULL, -- 'D02d', 'C25d', 'custom_fve'
|
||||
name TEXT NOT NULL,
|
||||
has_dual_rate BOOLEAN NOT NULL DEFAULT true, -- NT/VT nebo jednotarif
|
||||
vat_rate NUMERIC(5,4) NOT NULL DEFAULT 0.21,
|
||||
notes TEXT,
|
||||
UNIQUE (distributor, code)
|
||||
);
|
||||
|
||||
COMMENT ON TABLE ems.distribution_tariff IS
|
||||
'Číselník distribučních tarifů. Jeden záznam = jeden tarif jednoho distributora.
|
||||
has_dual_rate=true znamená NT/VT dvojsazba, false = jednotarif.';
|
||||
|
||||
-- ============================================================
|
||||
|
||||
CREATE TABLE ems.distribution_tariff_rate (
|
||||
id SERIAL PRIMARY KEY,
|
||||
tariff_id INT NOT NULL REFERENCES ems.distribution_tariff(id),
|
||||
rate_type TEXT NOT NULL, -- 'NT', 'VT', 'single'
|
||||
price_czk_kwh NUMERIC(10,6) NOT NULL, -- variabilní složka bez DPH
|
||||
valid_from DATE NOT NULL,
|
||||
valid_to DATE, -- NULL = platí dosud
|
||||
notes TEXT,
|
||||
UNIQUE (tariff_id, rate_type, valid_from)
|
||||
);
|
||||
|
||||
COMMENT ON TABLE ems.distribution_tariff_rate IS
|
||||
'Sazby distribučního tarifu Kč/kWh bez DPH. Verzováno přes valid_from/valid_to.
|
||||
Při roční změně tarifů: nastav valid_to na starém záznamu a přidej nový.
|
||||
price_czk_kwh = pouze variabilní distribuce, BEZ systémových služeb a OTE.';
|
||||
|
||||
COMMENT ON COLUMN ems.distribution_tariff_rate.price_czk_kwh IS
|
||||
'Variabilní distribuční složka Kč/kWh bez DPH.
|
||||
Nezahrnuje: systémové služby ČEPS, poplatek OTE, silovou elektřinu (spot).
|
||||
Tyto ostatní fixní složky jsou v site_market_config jako system_services_czk_kwh.';
|
||||
|
||||
-- ============================================================
|
||||
|
||||
CREATE TABLE ems.hdo_code (
|
||||
id SERIAL PRIMARY KEY,
|
||||
distributor TEXT NOT NULL, -- 'EGD', 'CEZ', 'PRE'
|
||||
code TEXT NOT NULL, -- 'B1', 'C3', 'custom_fve_home01'
|
||||
description TEXT,
|
||||
valid_from DATE NOT NULL,
|
||||
valid_to DATE, -- NULL = platí dosud
|
||||
notes TEXT,
|
||||
UNIQUE (distributor, code, valid_from)
|
||||
);
|
||||
|
||||
COMMENT ON TABLE ems.hdo_code IS
|
||||
'Číselník HDO kódů per distributor. Při roční změně přidat nový záznam
|
||||
s novým valid_from – starý zůstane v historii pro audit.
|
||||
Kód "custom_fve_home01" pro FVE instalace bez standardního HDO kódu.';
|
||||
|
||||
-- ============================================================
|
||||
|
||||
CREATE TABLE ems.hdo_code_window (
|
||||
id SERIAL PRIMARY KEY,
|
||||
hdo_code_id INT NOT NULL REFERENCES ems.hdo_code(id),
|
||||
day_type TEXT NOT NULL DEFAULT 'all',
|
||||
-- 'all' = každý den, 'workday' = Po-Pá, 'weekend' = So-Ne
|
||||
rate_type TEXT NOT NULL DEFAULT 'VT', -- 'VT' nebo 'NT'
|
||||
window_from TIME NOT NULL, -- začátek okna (inclusive)
|
||||
window_to TIME NOT NULL -- konec okna (exclusive)
|
||||
);
|
||||
|
||||
COMMENT ON TABLE ems.hdo_code_window IS
|
||||
'NT/VT časová okna pro HDO kód. Více řádků = více oken za den.
|
||||
Logika: pokud aktuální čas spadá do VT okna → rate_type=VT, jinak NT.
|
||||
day_type=all znamená stejná okna každý den (workday i weekend).
|
||||
Příklad home-01: VT 09:00-10:00, 12:00-13:00, 16:00-17:00, 20:00-21:00.';
|
||||
|
||||
-- ============================================================
|
||||
-- Rozšíření site_market_config
|
||||
-- ============================================================
|
||||
|
||||
ALTER TABLE ems.site_market_config
|
||||
ADD COLUMN IF NOT EXISTS tariff_id INT REFERENCES ems.distribution_tariff(id),
|
||||
ADD COLUMN IF NOT EXISTS hdo_code_id INT REFERENCES ems.hdo_code(id),
|
||||
ADD COLUMN IF NOT EXISTS system_services_czk_kwh NUMERIC(10,6) DEFAULT 0,
|
||||
ADD COLUMN IF NOT EXISTS ote_fee_czk_kwh NUMERIC(10,6) DEFAULT 0;
|
||||
|
||||
COMMENT ON COLUMN ems.site_market_config.tariff_id IS
|
||||
'Distribuční tarif přiřazený k této lokalitě. FK na distribution_tariff.';
|
||||
COMMENT ON COLUMN ems.site_market_config.hdo_code_id IS
|
||||
'HDO kód přiřazený k této lokalitě. Určuje NT/VT časová okna.
|
||||
Při změně HDO předpisu stačí přidat nový hdo_code záznam a aktualizovat FK.';
|
||||
COMMENT ON COLUMN ems.site_market_config.system_services_czk_kwh IS
|
||||
'Součet systémových poplatků Kč/kWh bez DPH:
|
||||
systémové služby ČEPS + poplatek OTE + příspěvek na OZE.
|
||||
Orientační hodnota EG.D 2025: ~0.40 Kč/kWh. Doplnit ze smlouvy/faktury.';
|
||||
COMMENT ON COLUMN ems.site_market_config.ote_fee_czk_kwh IS
|
||||
'Poplatek OTE za použití trhu Kč/kWh bez DPH.
|
||||
Orientačně ~0.001 Kč/kWh. Lze zahrnout do system_services_czk_kwh.';
|
||||
55
db/migration/V016__seed_distribution_home01.sql
Normal file
55
db/migration/V016__seed_distribution_home01.sql
Normal file
@@ -0,0 +1,55 @@
|
||||
-- EG.D tarif pro home-01 (FVE speciální režim)
|
||||
INSERT INTO ems.distribution_tariff
|
||||
(distributor, code, name, has_dual_rate, vat_rate, notes)
|
||||
VALUES
|
||||
('EGD', 'custom_fve_home01',
|
||||
'EG.D FVE vlastní spotřeba – dvojsazba',
|
||||
true, 0.21,
|
||||
'Speciální tarif pro FVE instalaci home-01. VT okna dle smlouvy.');
|
||||
|
||||
-- Sazby – placeholder hodnoty, doplnit ze smlouvy/faktury
|
||||
INSERT INTO ems.distribution_tariff_rate
|
||||
(tariff_id, rate_type, price_czk_kwh, valid_from)
|
||||
SELECT
|
||||
id, 'NT', 0.2243, '2025-01-01'
|
||||
FROM ems.distribution_tariff WHERE distributor='EGD' AND code='custom_fve_home01';
|
||||
|
||||
INSERT INTO ems.distribution_tariff_rate
|
||||
(tariff_id, rate_type, price_czk_kwh, valid_from)
|
||||
SELECT
|
||||
id, 'VT', 0.74987, '2025-01-01'
|
||||
FROM ems.distribution_tariff WHERE distributor='EGD' AND code='custom_fve_home01';
|
||||
|
||||
-- HDO kód pro home-01
|
||||
INSERT INTO ems.hdo_code
|
||||
(distributor, code, description, valid_from, notes)
|
||||
VALUES
|
||||
('EGD', 'custom_fve_home01',
|
||||
'FVE home-01 – VT okna 09-10, 12-13, 16-17, 20-21 (každý den)',
|
||||
'2025-01-01',
|
||||
'Platí stejně workday i weekend. Při změně přidat nový záznam s novým valid_from.');
|
||||
|
||||
-- VT okna (4 okna, každý den)
|
||||
INSERT INTO ems.hdo_code_window
|
||||
(hdo_code_id, day_type, rate_type, window_from, window_to)
|
||||
SELECT
|
||||
hc.id, 'all', 'VT', w.wf::TIME, w.wt::TIME
|
||||
FROM ems.hdo_code hc,
|
||||
(VALUES
|
||||
('09:00', '10:00'),
|
||||
('12:00', '13:00'),
|
||||
('16:00', '17:00'),
|
||||
('20:00', '21:00')
|
||||
) AS w(wf, wt)
|
||||
WHERE hc.distributor = 'EGD' AND hc.code = 'custom_fve_home01';
|
||||
|
||||
-- Napojit home-01 na tarif a HDO kód
|
||||
UPDATE ems.site_market_config SET
|
||||
tariff_id = (SELECT id FROM ems.distribution_tariff
|
||||
WHERE distributor='EGD' AND code='custom_fve_home01'),
|
||||
hdo_code_id = (SELECT id FROM ems.hdo_code
|
||||
WHERE distributor='EGD' AND code='custom_fve_home01'
|
||||
ORDER BY valid_from DESC LIMIT 1),
|
||||
system_services_czk_kwh = 0.192,
|
||||
ote_fee_czk_kwh = 0.001
|
||||
WHERE site_id = (SELECT id FROM ems.site WHERE code='home-01');
|
||||
47
db/migration/V017__green_bonus.sql
Normal file
47
db/migration/V017__green_bonus.sql
Normal file
@@ -0,0 +1,47 @@
|
||||
-- =============================================================
|
||||
-- V017__green_bonus.sql
|
||||
-- Zelený bonus na úrovni FVE pole (asset_pv_array), ne v prodejní ceně
|
||||
-- =============================================================
|
||||
|
||||
ALTER TABLE ems.asset_pv_array
|
||||
ADD COLUMN IF NOT EXISTS green_bonus_czk_kwh NUMERIC(10,6),
|
||||
ADD COLUMN IF NOT EXISTS green_bonus_valid_from DATE,
|
||||
ADD COLUMN IF NOT EXISTS green_bonus_valid_to DATE,
|
||||
ADD COLUMN IF NOT EXISTS green_bonus_meter_code TEXT;
|
||||
|
||||
COMMENT ON COLUMN ems.asset_pv_array.green_bonus_czk_kwh IS
|
||||
'Aktuální sazba zeleného bonusu Kč/kWh za vyrobenou elektřinu.
|
||||
NULL = pole nemá zelený bonus. Bonus se počítá z celkové výroby pole
|
||||
bez ohledu na to kam energie šla (interní spotřeba i export).
|
||||
Sazba se mění ročně – při změně nastav green_bonus_valid_to na starém
|
||||
záznamu a aktualizuj na novou hodnotu s novým green_bonus_valid_from.';
|
||||
|
||||
COMMENT ON COLUMN ems.asset_pv_array.green_bonus_valid_from IS
|
||||
'Datum od kdy platí aktuální sazba zeleného bonusu (včetně).';
|
||||
|
||||
COMMENT ON COLUMN ems.asset_pv_array.green_bonus_valid_to IS
|
||||
'Datum do kdy platí aktuální sazba zeleného bonusu (exclusive).
|
||||
NULL = platí dosud. Při roční změně nastav na první den nového roku
|
||||
a aktualizuj green_bonus_czk_kwh na novou sazbu.';
|
||||
|
||||
COMMENT ON COLUMN ems.asset_pv_array.green_bonus_meter_code IS
|
||||
'Číslo zeleného elektroměru (EAN nebo číslo ze smlouvy s distributorem).
|
||||
Slouží pro audit – bonus se počítá z odečtů tohoto elektroměru.';
|
||||
|
||||
ALTER TABLE ems.audit_interval
|
||||
ADD COLUMN IF NOT EXISTS green_bonus_czk NUMERIC(10,4) DEFAULT 0;
|
||||
|
||||
COMMENT ON COLUMN ems.audit_interval.green_bonus_czk IS
|
||||
'Příjem ze zeleného bonusu za výrobu bonusových FVE polí v Kč.
|
||||
Počítáno přes fn_green_bonus_revenue() v audit_filler.
|
||||
Nezahrnuto v actual_cost_czk – je to samostatný příjem.';
|
||||
|
||||
-- Seed home-01: zelený bonus jen na pv-b (ongrid střídač na GEN portu)
|
||||
UPDATE ems.asset_pv_array
|
||||
SET
|
||||
green_bonus_czk_kwh = 7.135, -- TODO: doplnit skutečnou sazbu ze smlouvy
|
||||
green_bonus_valid_from = '2026-01-01',
|
||||
green_bonus_valid_to = NULL, -- platí dosud
|
||||
green_bonus_meter_code = 'TODO' -- doplnit EAN zeleného elektroměru
|
||||
WHERE site_id = (SELECT id FROM ems.site WHERE code = 'home-01')
|
||||
AND code = 'pv-b';
|
||||
13
db/migration/V018__cleanup_legacy_green_bonus.sql
Normal file
13
db/migration/V018__cleanup_legacy_green_bonus.sql
Normal file
@@ -0,0 +1,13 @@
|
||||
-- =============================================================
|
||||
-- V018__cleanup_legacy_green_bonus.sql
|
||||
-- Odstranění legacy sloupců zeleného bonusu ze site_market_config (nahrazeno V017 asset_pv_array)
|
||||
-- =============================================================
|
||||
|
||||
ALTER TABLE ems.site_market_config
|
||||
DROP COLUMN IF EXISTS green_bonus_czk_kwh,
|
||||
DROP COLUMN IF EXISTS green_bonus_asset_code;
|
||||
|
||||
COMMENT ON TABLE ems.site_market_config IS
|
||||
'Konfigurace tržního prostředí per site.
|
||||
Zelený bonus je od V017 na ems.asset_pv_array (green_bonus_czk_kwh),
|
||||
nikoliv zde – bonus je vlastností fyzického FVE pole, ne site konfigurace.';
|
||||
43
db/migration/V019__forecast_accuracy.sql
Normal file
43
db/migration/V019__forecast_accuracy.sql
Normal file
@@ -0,0 +1,43 @@
|
||||
-- ============================================================
|
||||
-- Tabulka pro tracking přesnosti forecastu
|
||||
-- ============================================================
|
||||
|
||||
CREATE TABLE ems.forecast_accuracy (
|
||||
id SERIAL PRIMARY KEY,
|
||||
site_id INT NOT NULL REFERENCES ems.site(id),
|
||||
pv_array_id INT NOT NULL REFERENCES ems.asset_pv_array(id),
|
||||
interval_start TIMESTAMPTZ NOT NULL,
|
||||
run_id INT NOT NULL REFERENCES ems.forecast_pv_run(id),
|
||||
-- Forecast hodnoty
|
||||
forecast_power_w INT NOT NULL,
|
||||
forecast_created_at TIMESTAMPTZ NOT NULL,
|
||||
lead_time_hours NUMERIC(6,2), -- kolik hodin předem byl forecast vytvořen
|
||||
-- Skutečnost (doplněna zpětně z telemetrie)
|
||||
actual_power_w INT,
|
||||
actual_filled_at TIMESTAMPTZ,
|
||||
-- Odchylka
|
||||
error_w INT, -- forecast - actual
|
||||
error_pct NUMERIC(8,4), -- (forecast - actual) / actual * 100
|
||||
UNIQUE (run_id, interval_start)
|
||||
);
|
||||
|
||||
COMMENT ON TABLE ems.forecast_accuracy IS
|
||||
'Tracking přesnosti FVE forecastu. Každý řádek = jeden 15min slot
|
||||
z jednoho forecast runu. actual_power_w se doplňuje zpětně z telemetrie
|
||||
po uplynutí intervalu přes fn_fill_forecast_accuracy().
|
||||
Uchovávat navždy – slouží pro analýzu přesnosti a budoucí kalibraci solveru.';
|
||||
|
||||
COMMENT ON COLUMN ems.forecast_accuracy.lead_time_hours IS
|
||||
'Kolik hodin předem byl tento forecast vytvořen.
|
||||
Příklad: forecast vytvořen v pondělí 14:00, interval ve středu 12:00 = 46h.
|
||||
Slouží pro analýzu: je 6h forecast přesnější než 48h forecast?';
|
||||
|
||||
COMMENT ON COLUMN ems.forecast_accuracy.error_pct IS
|
||||
'Relativní chyba v %. Kladná = forecast nadhodnotil, záporná = podhodnotil.
|
||||
NULL pokud actual_power_w = 0 (zamezení dělení nulou).';
|
||||
|
||||
CREATE INDEX idx_forecast_accuracy_site_time
|
||||
ON ems.forecast_accuracy (site_id, interval_start DESC);
|
||||
|
||||
CREATE INDEX idx_forecast_accuracy_array_lead
|
||||
ON ems.forecast_accuracy (pv_array_id, lead_time_hours, interval_start DESC);
|
||||
30
db/migration/V020__ev_arrival_stats.sql
Normal file
30
db/migration/V020__ev_arrival_stats.sql
Normal file
@@ -0,0 +1,30 @@
|
||||
-- Statistika příjezdů EV (den v týdnu × hodina); plní telemetry_collector při přechodu available → nabíjení.
|
||||
|
||||
CREATE TABLE ems.ev_arrival_stats (
|
||||
id SERIAL PRIMARY KEY,
|
||||
site_id INT NOT NULL REFERENCES ems.site(id),
|
||||
vehicle_id INT REFERENCES ems.asset_vehicle(id),
|
||||
charger_id INT NOT NULL REFERENCES ems.asset_ev_charger(id),
|
||||
day_of_week INT NOT NULL,
|
||||
arrival_hour INT NOT NULL,
|
||||
sample_count INT NOT NULL DEFAULT 0,
|
||||
last_updated TIMESTAMPTZ NOT NULL DEFAULT now(),
|
||||
CONSTRAINT chk_ev_arrival_stats_dow CHECK (day_of_week >= 0 AND day_of_week <= 6),
|
||||
CONSTRAINT chk_ev_arrival_stats_hour CHECK (arrival_hour >= 0 AND arrival_hour <= 23),
|
||||
UNIQUE (site_id, charger_id, day_of_week, arrival_hour)
|
||||
);
|
||||
|
||||
CREATE INDEX idx_ev_arrival_stats_site_charger
|
||||
ON ems.ev_arrival_stats (site_id, charger_id);
|
||||
|
||||
COMMENT ON TABLE ems.ev_arrival_stats IS
|
||||
'Statistika příjezdů EV dle dne v týdnu a hodiny (časová zóna Europe/Prague).
|
||||
Plní se z ev_session / telemetrie při detekci připojení (available → preparing/charging).
|
||||
Po ~4 týdnech dat lze odhadovat typickou hodinu příjezdu.';
|
||||
|
||||
-- Nejvýše jedna otevřená session na nabíječku (pro INSERT … ON CONFLICT při startu session).
|
||||
CREATE UNIQUE INDEX uidx_ev_session_charger_open
|
||||
ON ems.ev_session (charger_id)
|
||||
WHERE session_end IS NULL;
|
||||
|
||||
GRANT SELECT ON ems.ev_arrival_stats TO ems_anon;
|
||||
27
db/migration/V021__baseline_consumption.sql
Normal file
27
db/migration/V021__baseline_consumption.sql
Normal file
@@ -0,0 +1,27 @@
|
||||
-- Historické průměry bazální spotřeby (DOW + hodina) pro solver a forecast.
|
||||
|
||||
CREATE TABLE ems.consumption_baseline_stats (
|
||||
id SERIAL PRIMARY KEY,
|
||||
site_id INT NOT NULL REFERENCES ems.site(id),
|
||||
day_of_week INT NOT NULL, -- 0=neděle, 1=pondělí... 6=sobota
|
||||
hour_of_day INT NOT NULL, -- 0-23
|
||||
avg_power_w NUMERIC(10,2) NOT NULL,
|
||||
stddev_power_w NUMERIC(10,2),
|
||||
sample_count INT NOT NULL DEFAULT 0,
|
||||
last_updated TIMESTAMPTZ NOT NULL DEFAULT now(),
|
||||
UNIQUE (site_id, day_of_week, hour_of_day)
|
||||
);
|
||||
|
||||
COMMENT ON TABLE ems.consumption_baseline_stats IS
|
||||
'Historické průměry bazální spotřeby per den v týdnu a hodinu.
|
||||
Plní se automaticky z telemetrie přes fn_update_baseline_stats().
|
||||
Bazální = load_power_w - ev - tc (bez řízených zátěží).
|
||||
Používá se jako vstup do solveru pro predikci spotřeby.';
|
||||
|
||||
COMMENT ON COLUMN ems.consumption_baseline_stats.avg_power_w IS
|
||||
'Průměrný výkon bazální spotřeby W pro daný DOW+hodinu.
|
||||
Exponenciální klouzavý průměr – nová data mají větší váhu.';
|
||||
|
||||
COMMENT ON COLUMN ems.consumption_baseline_stats.stddev_power_w IS
|
||||
'Směrodatná odchylka W – míra variability spotřeby.
|
||||
Lze použít pro konzervativní odhad: avg + 0.5*stddev.';
|
||||
38
db/migration/V022__extended_planning.sql
Normal file
38
db/migration/V022__extended_planning.sql
Normal file
@@ -0,0 +1,38 @@
|
||||
-- Rozšířený horizont plánování: statistiky cen a TUV pro predikce za horizont OTE.
|
||||
|
||||
CREATE TABLE ems.market_price_stats (
|
||||
id SERIAL PRIMARY KEY,
|
||||
site_id INT NOT NULL REFERENCES ems.site(id),
|
||||
day_of_week INT NOT NULL,
|
||||
hour_of_day INT NOT NULL,
|
||||
avg_price NUMERIC(10,6) NOT NULL,
|
||||
stddev_price NUMERIC(10,6),
|
||||
p25_price NUMERIC(10,6),
|
||||
p75_price NUMERIC(10,6),
|
||||
sample_count INT NOT NULL DEFAULT 0,
|
||||
last_updated TIMESTAMPTZ NOT NULL DEFAULT now(),
|
||||
UNIQUE (site_id, day_of_week, hour_of_day)
|
||||
);
|
||||
|
||||
COMMENT ON TABLE ems.market_price_stats IS
|
||||
'Historické průměry spotové ceny OTE per DOW+hodina.
|
||||
Analogie consumption_baseline_stats pro ceny.
|
||||
Používá se pro predikci cen za horizont OTE (36h+).
|
||||
Min. 3 měsíce dat pro smysluplné průměry.';
|
||||
|
||||
CREATE TABLE ems.tuv_usage_stats (
|
||||
id SERIAL PRIMARY KEY,
|
||||
site_id INT NOT NULL REFERENCES ems.site(id),
|
||||
day_of_week INT NOT NULL,
|
||||
hour_of_day INT NOT NULL,
|
||||
avg_temp_delta_c NUMERIC(6,3) NOT NULL,
|
||||
stddev_temp_delta NUMERIC(6,3),
|
||||
sample_count INT NOT NULL DEFAULT 0,
|
||||
last_updated TIMESTAMPTZ NOT NULL DEFAULT now(),
|
||||
UNIQUE (site_id, day_of_week, hour_of_day)
|
||||
);
|
||||
|
||||
COMMENT ON TABLE ems.tuv_usage_stats IS
|
||||
'Průměrná změna teploty TUV zásobníku per DOW+hodina.
|
||||
Záporná hodnota = zásobník se ochlazuje (spotřeba teplé vody).
|
||||
Kladná = TČ ohřívalo. Používá se pro predikci kdy bude potřeba ohřev.';
|
||||
54
db/migration/V023__modbus_command_journal.sql
Normal file
54
db/migration/V023__modbus_command_journal.sql
Normal file
@@ -0,0 +1,54 @@
|
||||
-- Modbus command journal + cut-off switch audit (EMS)
|
||||
|
||||
CREATE TABLE ems.modbus_command (
|
||||
id SERIAL PRIMARY KEY,
|
||||
site_id INT NOT NULL REFERENCES ems.site(id),
|
||||
asset_type TEXT NOT NULL, -- 'inverter', 'ev_charger', 'heat_pump', 'cutoff'
|
||||
asset_id INT NOT NULL, -- ID v příslušné asset tabulce
|
||||
asset_code TEXT NOT NULL, -- např. 'deye-main' (denorm. pro čitelnost)
|
||||
device_host TEXT NOT NULL,
|
||||
device_port INT NOT NULL,
|
||||
device_unit_id INT NOT NULL,
|
||||
register INT NOT NULL, -- číslo registru (decimal)
|
||||
register_name TEXT, -- 'export_limit', 'charge_limit' (pro čitelnost)
|
||||
value_to_write INT NOT NULL,
|
||||
value_written INT, -- skutečně zapsaná hodnota (NULL = nezapsáno)
|
||||
value_verified INT, -- přečtená hodnota po verifikaci (NULL = neověřeno)
|
||||
status TEXT NOT NULL DEFAULT 'pending',
|
||||
-- 'pending', 'written', 'verified', 'failed', 'mismatch', 'retrying'
|
||||
planning_run_id INT REFERENCES ems.planning_run(id),
|
||||
attempt_count INT NOT NULL DEFAULT 0,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
||||
written_at TIMESTAMPTZ,
|
||||
verified_at TIMESTAMPTZ,
|
||||
error_msg TEXT
|
||||
);
|
||||
|
||||
COMMENT ON TABLE ems.modbus_command IS
|
||||
'Command journal pro Modbus zápisy do zařízení.
|
||||
Každý zápis = jeden řádek. Verifikační job ověří value_verified == value_to_write.
|
||||
Při mismatch: retry max 3× → přepnout na SELF_SUSTAIN + Discord alert.
|
||||
asset_type+asset_id odkazuje na příslušnou asset tabulku (inverter/ev_charger/...).';
|
||||
|
||||
CREATE INDEX idx_modbus_command_status
|
||||
ON ems.modbus_command (site_id, status, created_at DESC);
|
||||
|
||||
CREATE INDEX idx_modbus_command_pending
|
||||
ON ems.modbus_command (status, created_at)
|
||||
WHERE status IN ('pending', 'retrying');
|
||||
|
||||
CREATE TABLE ems.cutoff_switch_log (
|
||||
id SERIAL PRIMARY KEY,
|
||||
site_id INT NOT NULL REFERENCES ems.site(id),
|
||||
asset_code TEXT NOT NULL, -- 'cutoff-pv-b', 'cutoff-microinverter'
|
||||
switched_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
||||
new_state BOOLEAN NOT NULL, -- true=zapnuto/připojeno, false=odpojeno
|
||||
previous_state BOOLEAN,
|
||||
reason TEXT NOT NULL, -- 'negative_sell_price', 'manual', 'auto_restore'
|
||||
sell_price_czk NUMERIC(10,6), -- spotová cena v době přepnutí
|
||||
triggered_by TEXT -- 'control_exporter', 'user:jan', 'system'
|
||||
);
|
||||
|
||||
COMMENT ON TABLE ems.cutoff_switch_log IS
|
||||
'Log přepnutí cut-off přepínačů. Loguje se jen při změně stavu (edge trigger).
|
||||
Používá se pro mikroinvertory na GEN portu při záporných prodejních cenách.';
|
||||
8
db/migration/V024__planning_predicted_price.sql
Normal file
8
db/migration/V024__planning_predicted_price.sql
Normal file
@@ -0,0 +1,8 @@
|
||||
-- Označení intervalů plánu, kde solver použil predikovanou cenu (mimo přesné OTE v efektivní ceně).
|
||||
|
||||
ALTER TABLE ems.planning_interval
|
||||
ADD COLUMN IF NOT EXISTS is_predicted_price BOOLEAN NOT NULL DEFAULT false;
|
||||
|
||||
COMMENT ON COLUMN ems.planning_interval.is_predicted_price IS
|
||||
'True pokud cena pro tento slot pochází z predikce (market_price_stats)
|
||||
a ne z přesných OTE dat. Sloty > 36h od now() při daily_plan běhu.';
|
||||
8
db/migration/V025__deye_physical_mode.sql
Normal file
8
db/migration/V025__deye_physical_mode.sql
Normal file
@@ -0,0 +1,8 @@
|
||||
-- Fyzický režim Deye u záznamů journalu (PASSIVE / SELL / CHARGE)
|
||||
|
||||
ALTER TABLE ems.modbus_command
|
||||
ADD COLUMN IF NOT EXISTS deye_physical_mode TEXT;
|
||||
|
||||
COMMENT ON COLUMN ems.modbus_command.deye_physical_mode IS
|
||||
'Fyzický režim Deye při zápisu: PASSIVE / SELL / CHARGE.
|
||||
Slouží pro audit a analýzu přepínání režimů.';
|
||||
11
db/migration/V026__battery_economics_tuning.sql
Normal file
11
db/migration/V026__battery_economics_tuning.sql
Normal file
@@ -0,0 +1,11 @@
|
||||
-- Tune battery economics for planning behavior:
|
||||
-- - lower reserve SOC to allow economically justified discharge
|
||||
-- - lower degradation cost to avoid overly conservative cycling
|
||||
--
|
||||
-- Idempotent update for currently deployed sites.
|
||||
UPDATE ems.asset_battery
|
||||
SET
|
||||
reserve_soc_percent = 10.00,
|
||||
degradation_cost_czk_kwh = 0.1500
|
||||
WHERE reserve_soc_percent <> 10.00
|
||||
OR degradation_cost_czk_kwh <> 0.1500;
|
||||
Reference in New Issue
Block a user