feat(pool): řízení bazénu Phase 1 — nejlevnější okno + dump-load (bez solveru)

fn_pool_schedule_slot: nejlevnější souvislé okno denního runtime budgetu
(fn_pool_daily_runtime_min) z vw_site_effective_price + dump-load při sell<=0.
fn_pool_control_tick: každých 15 min spočte stav a zařadí POOL_PUMP_ON (jen když
existuje signal_route → bezpečné před aktivací). lifespan job pool_control.
Shelly přes signal_service, žádné Modbus. Bazál odečet (R__003) se tím stává
správným (řízená+plánovaná zátěž). Aktivace provozně: daily_runtime_min=480,
schedulable, signal_route.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Dusan Vojacek
2026-06-14 22:00:49 +02:00
parent 8ffe5460f1
commit f70111f44b
3 changed files with 210 additions and 0 deletions

View File

@@ -161,6 +161,22 @@ async def lifespan(app: FastAPI):
except Exception:
logger.exception("scheduled_signal_outbound_verify failed")
async def scheduled_pool_control() -> None:
# Bazén: SQL-first rozhodnutí (fn_pool_control_tick) — nejlevnější souvislé
# okno denního runtime + dump-load při sell<=0; zařadí POOL_PUMP_ON (jen když
# existuje signal_route). Doručení řeší signal_outbound_send. Žádné Modbus.
try:
async with app.state.pg_pool.acquire() as conn:
rows = await conn.fetch("select * from ems.fn_pool_control_tick()")
for r in rows:
logger.info(
"pool control site=%s pump=%s on=%s runtime_min=%s route=%s enq=%s",
r["site_id"], r["pump_id"], r["desired_on"],
r["runtime_min"], r["has_route"], r["enqueued"],
)
except Exception:
logger.exception("scheduled_pool_control failed")
async def scheduled_verify_modbus() -> None:
"""
Ověří příkazy ve stavu written z posledních 20 minut.
@@ -413,6 +429,14 @@ async def lifespan(app: FastAPI):
id="signal_outbound_verify",
replace_existing=True,
)
scheduler.add_job(
scheduled_pool_control,
"cron",
minute="*/15",
second=2,
id="pool_control",
replace_existing=True,
)
scheduler.add_job(scheduled_daily_plan, "cron", hour=15, minute=0, id="daily_plan")
scheduler.add_job(
scheduled_rolling_replan,