Initial commit
Made-with: Cursor
This commit is contained in:
69
db/views/R__vw_audit_summary.sql
Normal file
69
db/views/R__vw_audit_summary.sql
Normal file
@@ -0,0 +1,69 @@
|
||||
-- =============================================================
|
||||
-- R__vw_audit_summary.sql
|
||||
-- EMS Platform – přehledové views pro audit a dashboard
|
||||
-- Repeatable migration
|
||||
-- =============================================================
|
||||
|
||||
-- Denní souhrn per lokalita
|
||||
CREATE OR REPLACE VIEW ems.vw_audit_daily AS
|
||||
SELECT
|
||||
site_id,
|
||||
date_trunc('day', interval_start AT TIME ZONE 'Europe/Prague') AS day_local,
|
||||
COUNT(*) AS interval_count,
|
||||
-- Energie (kWh = W × 15min / 60 = W / 4 / 1000)
|
||||
ROUND(SUM(GREATEST(actual_grid_power_w, 0))::NUMERIC / 4000, 3) AS import_kwh,
|
||||
ROUND(SUM(ABS(LEAST(actual_grid_power_w, 0)))::NUMERIC / 4000, 3) AS export_kwh,
|
||||
ROUND(SUM(GREATEST(actual_pv_power_w, 0))::NUMERIC / 4000, 3) AS pv_kwh,
|
||||
ROUND(SUM(GREATEST(actual_load_power_w, 0))::NUMERIC / 4000, 3) AS load_kwh,
|
||||
ROUND(SUM(GREATEST(actual_ev_power_w, 0))::NUMERIC / 4000, 3) AS ev_kwh,
|
||||
ROUND(SUM(GREATEST(actual_heat_pump_power_w, 0))::NUMERIC / 4000, 3) AS hp_kwh,
|
||||
-- Náklady
|
||||
ROUND(SUM(actual_cost_czk), 2) AS actual_cost_czk,
|
||||
ROUND(SUM(deviation_cost_czk), 2) AS total_deviation_czk,
|
||||
-- Počet intervalů s velkými odchylkami (>1kW)
|
||||
COUNT(*) FILTER (WHERE ABS(deviation_grid_w) > 1000) AS high_deviation_count
|
||||
FROM ems.audit_interval
|
||||
GROUP BY site_id, date_trunc('day', interval_start AT TIME ZONE 'Europe/Prague');
|
||||
|
||||
COMMENT ON VIEW ems.vw_audit_daily IS
|
||||
'Denní souhrn auditu per lokalita. Energie v kWh, náklady v Kč.
|
||||
Používat pro dashboard denního přehledu a reporty.';
|
||||
|
||||
-- ============================================================
|
||||
|
||||
-- Týdenní souhrn
|
||||
CREATE OR REPLACE VIEW ems.vw_audit_weekly AS
|
||||
SELECT
|
||||
site_id,
|
||||
date_trunc('week', interval_start AT TIME ZONE 'Europe/Prague') AS week_local,
|
||||
ROUND(SUM(GREATEST(actual_grid_power_w, 0))::NUMERIC / 4000, 1) AS import_kwh,
|
||||
ROUND(SUM(ABS(LEAST(actual_grid_power_w, 0)))::NUMERIC / 4000, 1) AS export_kwh,
|
||||
ROUND(SUM(GREATEST(actual_pv_power_w, 0))::NUMERIC / 4000, 1) AS pv_kwh,
|
||||
ROUND(SUM(actual_cost_czk), 0) AS actual_cost_czk
|
||||
FROM ems.audit_interval
|
||||
GROUP BY site_id, date_trunc('week', interval_start AT TIME ZONE 'Europe/Prague');
|
||||
|
||||
COMMENT ON VIEW ems.vw_audit_weekly IS
|
||||
'Týdenní souhrn auditu per lokalita.';
|
||||
|
||||
-- ============================================================
|
||||
|
||||
-- Aktuální den – hourly breakdown pro dashboard graf
|
||||
CREATE OR REPLACE VIEW ems.vw_audit_today_hourly AS
|
||||
SELECT
|
||||
site_id,
|
||||
date_trunc('hour', interval_start AT TIME ZONE 'Europe/Prague') AS hour_local,
|
||||
ROUND(AVG(actual_pv_power_w)::NUMERIC / 1000, 2) AS avg_pv_kw,
|
||||
ROUND(AVG(actual_battery_power_w)::NUMERIC / 1000, 2) AS avg_battery_kw,
|
||||
ROUND(AVG(actual_grid_power_w)::NUMERIC / 1000, 2) AS avg_grid_kw,
|
||||
ROUND(AVG(actual_load_power_w)::NUMERIC / 1000, 2) AS avg_load_kw,
|
||||
ROUND(AVG(actual_battery_soc_pct), 1) AS avg_soc_pct,
|
||||
ROUND(SUM(actual_cost_czk), 2) AS cost_czk
|
||||
FROM ems.audit_interval
|
||||
WHERE interval_start >= date_trunc('day', now() AT TIME ZONE 'Europe/Prague')
|
||||
AND interval_start < date_trunc('day', now() AT TIME ZONE 'Europe/Prague') + INTERVAL '1 day'
|
||||
GROUP BY site_id, date_trunc('hour', interval_start AT TIME ZONE 'Europe/Prague')
|
||||
ORDER BY hour_local;
|
||||
|
||||
COMMENT ON VIEW ems.vw_audit_today_hourly IS
|
||||
'Hodinový přehled dnešního dne pro dashboard graf výkonů a nákladů.';
|
||||
77
db/views/R__vw_latest_telemetry.sql
Normal file
77
db/views/R__vw_latest_telemetry.sql
Normal file
@@ -0,0 +1,77 @@
|
||||
-- =============================================================
|
||||
-- R__vw_latest_telemetry.sql
|
||||
-- EMS Platform – aktuální stav všech zařízení per lokalita
|
||||
-- Repeatable migration
|
||||
-- =============================================================
|
||||
|
||||
CREATE OR REPLACE VIEW ems.vw_latest_inverter AS
|
||||
SELECT DISTINCT ON (t.inverter_id)
|
||||
t.site_id,
|
||||
t.inverter_id,
|
||||
inv.code AS inverter_code,
|
||||
t.measured_at,
|
||||
t.pv_power_w,
|
||||
t.battery_soc_percent,
|
||||
t.battery_power_w,
|
||||
t.grid_power_w,
|
||||
t.load_power_w,
|
||||
t.inverter_temp_c,
|
||||
t.operating_mode,
|
||||
t.fault_code,
|
||||
now() - t.measured_at AS data_age
|
||||
FROM ems.telemetry_inverter t
|
||||
JOIN ems.asset_inverter inv ON inv.id = t.inverter_id
|
||||
ORDER BY t.inverter_id, t.measured_at DESC;
|
||||
|
||||
COMMENT ON VIEW ems.vw_latest_inverter IS
|
||||
'Nejnovější telemetrická data pro každý střídač. Slouží pro real-time dashboard a health check.';
|
||||
|
||||
-- ------------------------------------------------------------
|
||||
|
||||
CREATE OR REPLACE VIEW ems.vw_latest_ev_charger AS
|
||||
SELECT DISTINCT ON (t.charger_id, t.connector_id)
|
||||
t.site_id,
|
||||
t.charger_id,
|
||||
ch.code AS charger_code,
|
||||
t.connector_id,
|
||||
t.measured_at,
|
||||
t.status,
|
||||
t.power_w,
|
||||
t.energy_kwh,
|
||||
t.current_a,
|
||||
t.session_id,
|
||||
t.error_code,
|
||||
now() - t.measured_at AS data_age
|
||||
FROM ems.telemetry_ev_charger t
|
||||
JOIN ems.asset_ev_charger ch ON ch.id = t.charger_id
|
||||
ORDER BY t.charger_id, t.connector_id, t.measured_at DESC;
|
||||
|
||||
COMMENT ON VIEW ems.vw_latest_ev_charger IS
|
||||
'Nejnovější telemetrická data pro každý konektor EV nabíječky. Slouží pro dashboard a řízení nabíjení.';
|
||||
|
||||
-- ------------------------------------------------------------
|
||||
|
||||
CREATE OR REPLACE VIEW ems.vw_latest_heat_pump AS
|
||||
SELECT DISTINCT ON (t.heat_pump_id)
|
||||
t.site_id,
|
||||
t.heat_pump_id,
|
||||
hp.code AS heat_pump_code,
|
||||
t.measured_at,
|
||||
t.outdoor_temp_c,
|
||||
t.tuv_tank_temp_c,
|
||||
t.water_outlet_temp_c,
|
||||
t.power_w,
|
||||
t.operating_mode,
|
||||
t.cop_actual,
|
||||
t.defrost_active,
|
||||
t.alarm_code,
|
||||
-- Odhadovaný COP pro aktuální venkovní teplotu
|
||||
ems.fn_cop_estimate(t.heat_pump_id, t.outdoor_temp_c) AS cop_estimated,
|
||||
now() - t.measured_at AS data_age
|
||||
FROM ems.telemetry_heat_pump t
|
||||
JOIN ems.asset_heat_pump hp ON hp.id = t.heat_pump_id
|
||||
ORDER BY t.heat_pump_id, t.measured_at DESC;
|
||||
|
||||
COMMENT ON VIEW ems.vw_latest_heat_pump IS
|
||||
'Nejnovější telemetrická data pro každé tepelné čerpadlo včetně odhadovaného COP.
|
||||
Slouží pro real-time dashboard a rozhodovací logiku plánování.';
|
||||
75
db/views/R__vw_operating_mode.sql
Normal file
75
db/views/R__vw_operating_mode.sql
Normal file
@@ -0,0 +1,75 @@
|
||||
-- =============================================================
|
||||
-- R__vw_operating_mode.sql
|
||||
-- EMS Platform – views pro provozní režimy a heartbeat
|
||||
-- Repeatable migration
|
||||
-- =============================================================
|
||||
|
||||
-- Aktuální stav všech lokalit (pro dashboard a PostgREST)
|
||||
CREATE OR REPLACE VIEW ems.vw_site_status AS
|
||||
SELECT
|
||||
s.id AS site_id,
|
||||
s.code AS site_code,
|
||||
s.name AS site_name,
|
||||
m.mode_code AS active_mode,
|
||||
d.name AS mode_name,
|
||||
d.description AS mode_description,
|
||||
d.is_autonomous,
|
||||
m.activated_at,
|
||||
m.activated_by,
|
||||
m.valid_until,
|
||||
m.previous_mode,
|
||||
m.notes AS mode_notes,
|
||||
-- Heartbeat
|
||||
hb.last_seen AS ems_last_seen,
|
||||
hb.status AS ems_status,
|
||||
EXTRACT(EPOCH FROM (now() - hb.last_seen))::INT AS ems_age_seconds,
|
||||
-- Varování pokud EMS dlouho nepingoval
|
||||
CASE
|
||||
WHEN hb.last_seen IS NULL THEN 'never_seen'
|
||||
WHEN now() - hb.last_seen > INTERVAL '5 minutes' THEN 'stale'
|
||||
WHEN now() - hb.last_seen > INTERVAL '2 minutes' THEN 'delayed'
|
||||
ELSE 'ok'
|
||||
END AS ems_heartbeat_status,
|
||||
-- Aktuální telemetrie (snapshot)
|
||||
li.pv_power_w,
|
||||
li.battery_soc_percent,
|
||||
li.battery_power_w,
|
||||
li.grid_power_w,
|
||||
li.load_power_w,
|
||||
li.measured_at AS telemetry_at
|
||||
FROM ems.site s
|
||||
LEFT JOIN ems.site_operating_mode m ON m.site_id = s.id
|
||||
LEFT JOIN ems.operating_mode_def d ON d.code = m.mode_code
|
||||
LEFT JOIN ems.site_heartbeat hb ON hb.site_id = s.id
|
||||
LEFT JOIN ems.vw_latest_inverter li ON li.site_id = s.id
|
||||
WHERE s.active = true;
|
||||
|
||||
COMMENT ON VIEW ems.vw_site_status IS
|
||||
'Kompletní stavový přehled lokality: aktivní režim, heartbeat EMS, aktuální telemetrie.
|
||||
Primární view pro dashboard a health check endpoint. Jeden řádek na aktivní lokalitu.
|
||||
ems_heartbeat_status slouží pro EMS vlastní alerting – Loxone watchdog tuto tabulku nečte,
|
||||
sleduje HTTP pulzy přímo (viz docs/loxone-integration.md).';
|
||||
|
||||
-- ============================================================
|
||||
|
||||
-- Log přepnutí režimů (pro UI historii)
|
||||
CREATE OR REPLACE VIEW ems.vw_mode_log_recent AS
|
||||
SELECT
|
||||
l.id,
|
||||
l.site_id,
|
||||
s.code AS site_code,
|
||||
l.mode_code,
|
||||
d.name AS mode_name,
|
||||
l.activated_at,
|
||||
l.deactivated_at,
|
||||
EXTRACT(EPOCH FROM COALESCE(l.deactivated_at, now()) - l.activated_at)::INT AS duration_sec,
|
||||
l.activated_by,
|
||||
l.notes
|
||||
FROM ems.site_operating_mode_log l
|
||||
JOIN ems.site s ON s.id = l.site_id
|
||||
JOIN ems.operating_mode_def d ON d.code = l.mode_code
|
||||
WHERE l.activated_at >= now() - INTERVAL '7 days'
|
||||
ORDER BY l.activated_at DESC;
|
||||
|
||||
COMMENT ON VIEW ems.vw_mode_log_recent IS
|
||||
'Posledních 7 dní přepnutí provozních režimů. Slouží pro audit log v UI.';
|
||||
42
db/views/R__vw_site_effective_price.sql
Normal file
42
db/views/R__vw_site_effective_price.sql
Normal file
@@ -0,0 +1,42 @@
|
||||
-- =============================================================
|
||||
-- R__vw_site_effective_price.sql
|
||||
-- EMS Platform – view efektivních cen per site
|
||||
-- Repeatable migration
|
||||
-- =============================================================
|
||||
|
||||
CREATE OR REPLACE VIEW ems.vw_site_effective_price AS
|
||||
SELECT
|
||||
smc.site_id,
|
||||
mip.interval_start,
|
||||
mip.interval_end,
|
||||
mip.market_source,
|
||||
-- Raw ceny
|
||||
mip.buy_raw_price_czk_kwh,
|
||||
mip.sell_raw_price_czk_kwh,
|
||||
-- Marže
|
||||
smc.buy_margin_fixed_czk,
|
||||
smc.buy_margin_percent,
|
||||
smc.sell_margin_fixed_czk,
|
||||
smc.sell_margin_percent,
|
||||
-- Efektivní ceny
|
||||
ROUND(
|
||||
mip.buy_raw_price_czk_kwh
|
||||
+ smc.buy_margin_fixed_czk
|
||||
+ (mip.buy_raw_price_czk_kwh * smc.buy_margin_percent / 100.0),
|
||||
6
|
||||
) AS effective_buy_price_czk_kwh,
|
||||
ROUND(
|
||||
mip.sell_raw_price_czk_kwh
|
||||
+ smc.sell_margin_fixed_czk
|
||||
+ (mip.sell_raw_price_czk_kwh * smc.sell_margin_percent / 100.0),
|
||||
6
|
||||
) AS effective_sell_price_czk_kwh
|
||||
FROM ems.market_interval_price mip
|
||||
CROSS JOIN ems.site_market_config smc
|
||||
WHERE smc.valid_from <= mip.interval_start
|
||||
AND (smc.valid_to IS NULL OR smc.valid_to > mip.interval_start);
|
||||
|
||||
COMMENT ON VIEW ems.vw_site_effective_price IS
|
||||
'Efektivní nákupní a prodejní ceny elektřiny per lokalita a 15min interval.
|
||||
Dopočítává marže z site_market_config na raw ceny z market_interval_price.
|
||||
Nezahrnuje data bez platné market_config. Používat pro plánování a audit.';
|
||||
Reference in New Issue
Block a user