# Bazénové čerpadlo (Shelly) — řízení

Řízená zátěž: filtrační čerpadlo bazénu přes Shelly Plug S Gen3 (relé). EMS ho
spíná podle cen, čte telemetrii a započítává do plánu.

## Datový model (V087, V092)

- `ems.asset_pool_pump` — `rated_power_w`, `min_run_min`, `daily_runtime_min`
  (statický cíl filtrace/den), `schedulable`, `shelly_switch_id`, `endpoint_id`,
  teplotní parametry: `runtime_base_min`, `runtime_min_per_c`, `runtime_ref_temp_c`,
  `runtime_min_min`, `runtime_max_min`, `water_temp_sensor_id`.
- `ems.telemetry_pool_pump` — 1min: `is_on`, `power_w`, `energy_wh_total` (hypertable).

## Denní runtime budget — `fn_pool_daily_runtime_min(pump_id)`

`clamp(runtime_base_min + runtime_min_per_c × (teplota − runtime_ref_temp_c),
runtime_min_min, runtime_max_min)` z poslední teploty vody (`telemetry_loxone_sensor`,
< 24 h). **Bez čidla** → fallback `daily_runtime_min` (např. 480 = 8 h). Teplotní
režim se zapne pouhým napojením `water_temp_sensor_id` — žádný kód navíc.

## Rozvrh do slotů (Phase 1, bez solveru) — `fn_pool_schedule_slot(pump_id, slot)`

Vrací ON/OFF pro 15min slot:
- **Nejlevnější souvislé okno** délky = runtime budget (z `vw_site_effective_price`,
  kalendářní den slotu v Europe/Prague, řazeno dle efektivní nákupní ceny). PV/záporné
  dny → okno padne automaticky přes poledne.
- **+ dump-load overlay:** `sell <= 0` (záporná/nulová výkupní cena) → ON i mimo okno
  (zkonzumuj přebytek místo exportu se ztrátou).
- `false` když pump není `schedulable` nebo nejsou ceny pro den.

## Control smyčka — `fn_pool_control_tick()` + APScheduler

Job `pool_control` (každých 15 min, hranice slotu, `lifespan.py`) volá
`fn_pool_control_tick()` → pro každý aktivní řiditelný bazén spočte
`fn_pool_schedule_slot` a **idempotentně** zařadí signál `POOL_PUMP_ON`
(`fn_signal_enqueue_bool`) — **jen když existuje `signal_route`** (jinak bezpečně
nic). Doručení na Shelly (`Switch.Set`) + readback verify (`Switch.GetStatus`) řeší
`signal_service` (každých 15 s). Žádné Modbus.

## Bazál

`fn_update_baseline_stats` (R__003) bazén **odečítá** z bazálu — to je správné
**jen** když ho zároveň řídíme (řízená + plánovaná zátěž). Bez řízení by to
plánovač oslepilo. S tímto řízením je odečet korektní.

## Aktivace (provozní, per site)

1. `asset_pool_pump.daily_runtime_min` = cílové minuty (480 = 8 h), `schedulable = true`.
2. Seed `signal_route` (`POOL_PUMP_ON` → `http_rest` na Shelly endpoint, `map_bool`
   true/false → on/off; `verify_config_json` přes `Switch.GetStatus`).
3. Ověřit `fn_pool_schedule_slot` vrací rozumné sloty + telemetrii Shelly, pak teprve
   ostře (control tick enqueueuje až s existující route).

## Roadmap

- **Phase 2:** `pool_on[t]` do solveru (`solver_v2`) — co-optimalizace proti
  baterii/exportu (golden gate). Dump-load pak z živého SoC/PV, ne jen z ceny.
- Teplotní čidlo: napojit `water_temp_sensor_id` → runtime se prodlouží/zkrátí dle
  teploty vody automaticky.
