Initial commit

Made-with: Cursor
This commit is contained in:
Dusan Vojacek
2026-03-20 13:27:37 +01:00
commit 8b4af663d8
77 changed files with 13337 additions and 0 deletions

View File

@@ -0,0 +1,117 @@
-- =============================================================
-- V004__operating_modes.sql
-- EMS Platform provozní režimy lokalit
-- =============================================================
-- ============================================================
-- Číselník provozních režimů
-- ============================================================
CREATE TABLE ems.operating_mode_def (
code TEXT PRIMARY KEY,
name TEXT NOT NULL,
description TEXT NOT NULL,
ev_enabled BOOLEAN NOT NULL DEFAULT false,
heat_pump_enabled BOOLEAN NOT NULL DEFAULT false,
battery_mode TEXT NOT NULL, -- 'plan', 'self_sustain', 'charge_max', 'hold', 'none'
grid_mode TEXT NOT NULL, -- 'plan', 'import_ok', 'no_import', 'no_export', 'none'
loxone_mode_value INT NOT NULL, -- integer posílaný do Loxone Virtual Input
is_autonomous BOOLEAN NOT NULL DEFAULT false, -- Loxone umí sám bez EMS setpointů
sort_order INT NOT NULL DEFAULT 50
);
COMMENT ON TABLE ems.operating_mode_def IS 'Číselník provozních režimů systému. Každý režim definuje chování baterie, sítě a flexibilních zařízení.';
COMMENT ON COLUMN ems.operating_mode_def.code IS 'Unikátní kód režimu. Příklad: AUTO, SELF_SUSTAIN.';
COMMENT ON COLUMN ems.operating_mode_def.name IS 'Lidsky čitelný název režimu pro UI.';
COMMENT ON COLUMN ems.operating_mode_def.description IS 'Popis chování v daném režimu.';
COMMENT ON COLUMN ems.operating_mode_def.ev_enabled IS 'Zda je povoleno nabíjení EV v tomto režimu.';
COMMENT ON COLUMN ems.operating_mode_def.heat_pump_enabled IS 'Zda je tepelné čerpadlo povoleno v tomto režimu.';
COMMENT ON COLUMN ems.operating_mode_def.battery_mode IS 'Chování baterie: plan=dle EMS plánu, self_sustain=vybíjí do domu, charge_max=max nabíjení, hold=drží SoC, none=žádné akce.';
COMMENT ON COLUMN ems.operating_mode_def.grid_mode IS 'Chování vůči síti: plan=dle EMS, import_ok=import povolen, no_import=bez importu, no_export=bez exportu, none=žádné akce.';
COMMENT ON COLUMN ems.operating_mode_def.loxone_mode_value IS 'Celočíselná hodnota posílaná do Loxone Virtual Input EMS_Mode. Loxone interně přepíná stavový stroj.';
COMMENT ON COLUMN ems.operating_mode_def.is_autonomous IS 'Pokud true, Loxone zvládne tento režim bez průběžných setpointů od EMS (fallback bezpečný).';
COMMENT ON COLUMN ems.operating_mode_def.sort_order IS 'Pořadí zobrazení v UI.';
INSERT INTO ems.operating_mode_def
(code, name, description, ev_enabled, heat_pump_enabled, battery_mode, grid_mode, loxone_mode_value, is_autonomous, sort_order)
VALUES
('AUTO', 'Automatický', 'EMS řídí vše podle optimalizovaného plánu. Setpointy posílány každých 15 min.',
true, true, 'plan', 'plan', 1, false, 10),
('SELF_SUSTAIN', 'Soběstačný', 'Fallback bez EMS. FVE + baterie pokrývají spotřebu. Žádný import, žádný export, EV a TČ odstaveny.',
false, false, 'self_sustain', 'no_export', 2, true, 20),
('CHARGE_CHEAP', 'Nabíjení levnou cenou', 'Manuální: max nabíjení baterie ze sítě. Použít při ručně identifikované levné ceně nebo akci.',
false, false, 'charge_max', 'import_ok', 3, false, 30),
('PRESERVE', 'Ochrana baterie', 'Dovolená / servis. Baterie drží aktuální SoC, žádné akce. EV a TČ odstaveny.',
false, false, 'hold', 'import_ok', 4, true, 40),
('MANUAL', 'Manuální', 'Technický přepis. EMS ani Loxone logika neřídí střídač. Pouze pro servisní práce.',
false, false, 'none', 'none', 0, true, 50);
-- ============================================================
-- Aktivní provozní režim per lokalita
-- ============================================================
CREATE TABLE ems.site_operating_mode (
site_id INT NOT NULL REFERENCES ems.site(id) PRIMARY KEY,
mode_code TEXT NOT NULL REFERENCES ems.operating_mode_def(code),
activated_at TIMESTAMPTZ NOT NULL DEFAULT now(),
activated_by TEXT,
valid_until TIMESTAMPTZ, -- NULL = platí do ručního přepnutí
previous_mode TEXT REFERENCES ems.operating_mode_def(code),
notes TEXT
);
COMMENT ON TABLE ems.site_operating_mode IS 'Aktuálně aktivní provozní režim per lokalita. Jeden řádek na lokalitu (upsert při přepnutí).';
COMMENT ON COLUMN ems.site_operating_mode.site_id IS 'Vazba na lokalitu. PK jedna lokalita má vždy právě jeden aktivní režim.';
COMMENT ON COLUMN ems.site_operating_mode.mode_code IS 'Aktuálně aktivní režim. FK na operating_mode_def.';
COMMENT ON COLUMN ems.site_operating_mode.activated_at IS 'Čas přepnutí do tohoto režimu.';
COMMENT ON COLUMN ems.site_operating_mode.activated_by IS 'Kdo nebo co přepnulo režim: user:jan, system:watchdog, api, loxone.';
COMMENT ON COLUMN ems.site_operating_mode.valid_until IS 'Automatické vypršení režimu. NULL = platí do ručního přepnutí.';
COMMENT ON COLUMN ems.site_operating_mode.previous_mode IS 'Předchozí režim pro rychlý návrat zpět.';
COMMENT ON COLUMN ems.site_operating_mode.notes IS 'Volný komentář k přepnutí.';
-- ============================================================
-- Historie přepnutí režimů (audit log)
-- ============================================================
CREATE TABLE ems.site_operating_mode_log (
id SERIAL PRIMARY KEY,
site_id INT NOT NULL REFERENCES ems.site(id),
mode_code TEXT NOT NULL REFERENCES ems.operating_mode_def(code),
activated_at TIMESTAMPTZ NOT NULL DEFAULT now(),
deactivated_at TIMESTAMPTZ,
activated_by TEXT,
notes TEXT
);
COMMENT ON TABLE ems.site_operating_mode_log IS 'Auditní log všech přepnutí provozních režimů per lokalita.';
COMMENT ON COLUMN ems.site_operating_mode_log.id IS 'Primární klíč.';
COMMENT ON COLUMN ems.site_operating_mode_log.site_id IS 'Vazba na lokalitu.';
COMMENT ON COLUMN ems.site_operating_mode_log.mode_code IS 'Aktivovaný režim.';
COMMENT ON COLUMN ems.site_operating_mode_log.activated_at IS 'Čas aktivace režimu.';
COMMENT ON COLUMN ems.site_operating_mode_log.deactivated_at IS 'Čas deaktivace (přepnutí na jiný režim). NULL = stále aktivní.';
COMMENT ON COLUMN ems.site_operating_mode_log.activated_by IS 'Zdroj přepnutí: user:jan, system:watchdog, system:ems_start.';
COMMENT ON COLUMN ems.site_operating_mode_log.notes IS 'Volný komentář.';
-- ============================================================
-- Heartbeat tabulka pouze informační, pro EMS vlastní diagnostiku
-- POZOR: Loxone watchdog NEČTE tuto tabulku.
-- Loxone sleduje HTTP pulzy přímo (nezávisle na DB).
-- Tato tabulka slouží jen pro EMS dashboard a alerting.
-- ============================================================
CREATE TABLE ems.site_heartbeat (
site_id INT NOT NULL REFERENCES ems.site(id) PRIMARY KEY,
last_seen TIMESTAMPTZ NOT NULL DEFAULT now(),
ems_version TEXT,
status TEXT NOT NULL DEFAULT 'ok'
);
COMMENT ON TABLE ems.site_heartbeat IS 'Informační záznam posledního úspěšného heartbeat pulzu EMS per lokalita. Slouží pouze pro EMS dashboard a alerting Loxone watchdog tuto tabulku nečte a nezávisí na ní. Fallback logika je implementována čistě v Loxone (viz docs/loxone-integration.md).';
COMMENT ON COLUMN ems.site_heartbeat.site_id IS 'Vazba na lokalitu. PK.';
COMMENT ON COLUMN ems.site_heartbeat.last_seen IS 'Čas kdy EMS backend naposledy úspěšně odeslal pulz do Loxone.';
COMMENT ON COLUMN ems.site_heartbeat.ems_version IS 'Verze EMS backendu pro diagnostiku.';
COMMENT ON COLUMN ems.site_heartbeat.status IS 'Stav EMS backendu: ok, degraded (částečný výpadek), error (kritická chyba).';