Bazén: sezóna (schedulable), filtrace dle teploty vody, Loxone čidla
All checks were successful
CI and deploy / migration-check (push) Successful in 42s
CI and deploy / deploy (push) Has been skipped

- V092: ems.loxone_sensor + telemetry_loxone_sensor (hypertable) — generické
  čtení Loxone hodnot (poslouží i ohřevu/akumulačce); pool sloupce teplotní
  funkce (ref/base/per_c/min/max) + water_temp_sensor_id
- R__098 fn_pool_daily_runtime_min: clamp(base+per_c×(t−ref)) z poslední
  teploty <24 h, fallback daily_runtime_min; JSON detail pro UI/solver
- collector poll_loxone_sensors: /jdev/sps/io/<name>/state, LL.value parse,
  no-op bez čidel
- sezóna = schedulable přepínač (dokumentováno vč. SQL); hranice filtrace ×
  ohřev TČ (oddělené logiky, sdílí jen čidlo)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Dusan Vojacek
2026-06-12 11:47:03 +02:00
parent f3eb16892f
commit 15d47e8a80
4 changed files with 188 additions and 0 deletions

View File

@@ -0,0 +1,55 @@
-- Bazén: sezóna, délka filtrace dle teploty vody, čtení čidel z Loxone.
--
-- Sezóna: přepínač = existující asset_pool_pump.schedulable (true = plánovač
-- řídí; konec sezóny -> false: telemetrie běží dál, signály/solver ne).
-- Viz docs/04-modules/pool-shelly.md § Sezóna.
--
-- Teplotní funkce (slaná voda, chlorinátor potřebuje průtok; teplejší voda =
-- delší filtrace): runtime_min(t) = clamp(base + per_c × (t ref), min, max).
-- Defaulty pro 30 m³ / 8 m³/h (obrátka 3.75 h): 20 °C → 4.5 h, 26 °C → 7.5 h,
-- 28 °C → 8.5 h, strop 10 h. Bez čidla / starého měření → fallback
-- daily_runtime_min. Vše per čerpadlo v DB (pravidlo 16).
create table ems.loxone_sensor (
id serial primary key,
site_id int not null references ems.site (id),
code text not null,
loxone_name text not null,
unit text,
enabled boolean not null default true,
notes text,
constraint uq_loxone_sensor_site_code unique (site_id, code)
);
comment on table ems.loxone_sensor is
'Čidla čtená z Loxone Miniserveru (GET /jdev/sps/io/<loxone_name>/state přes loxone_http endpoint site). Telemetrie 60 s do telemetry_loxone_sensor.';
create table ems.telemetry_loxone_sensor (
sensor_id int not null references ems.loxone_sensor (id),
measured_at timestamptz not null,
value numeric(10, 2),
primary key (sensor_id, measured_at)
);
select create_hypertable(
'ems.telemetry_loxone_sensor',
'measured_at',
chunk_time_interval => interval '1 week',
if_not_exists => true
);
comment on table ems.telemetry_loxone_sensor is
'1min hodnoty Loxone čidel (teplota bazénu, akumulační nádrže, ...).';
alter table ems.asset_pool_pump
add column if not exists water_temp_sensor_id int references ems.loxone_sensor (id),
add column if not exists runtime_ref_temp_c numeric(4, 1) not null default 20.0,
add column if not exists runtime_base_min int not null default 270,
add column if not exists runtime_min_per_c int not null default 30,
add column if not exists runtime_min_min int not null default 180,
add column if not exists runtime_max_min int not null default 600;
comment on column ems.asset_pool_pump.water_temp_sensor_id is
'Loxone čidlo teploty vody; NULL = teplotní funkce vypnutá (fallback daily_runtime_min).';
comment on column ems.asset_pool_pump.runtime_base_min is
'Minuty filtrace/den při runtime_ref_temp_c; nad ní +runtime_min_per_c za °C, clamp [runtime_min_min, runtime_max_min].';

View File

@@ -0,0 +1,45 @@
-- Denní cíl filtrace bazénu: dle teploty vody (poslední měření < 24 h),
-- jinak fallback daily_runtime_min. Vstup pro solver (pool_on[t] budget).
create or replace function ems.fn_pool_daily_runtime_min(p_pump_id int)
returns jsonb
language sql
stable
as $fn$
select jsonb_build_object(
'runtime_min',
coalesce(
case
when t.value is not null then
least(
pp.runtime_max_min,
greatest(
pp.runtime_min_min,
round(
pp.runtime_base_min
+ pp.runtime_min_per_c * greatest(0, t.value - pp.runtime_ref_temp_c)
)::int
)
)
end,
pp.daily_runtime_min
),
'water_temp_c', t.value,
'temp_measured_at', t.measured_at,
'source', case when t.value is not null then 'temp_function' else 'static' end,
'schedulable', pp.schedulable
)
from ems.asset_pool_pump pp
left join lateral (
select ts.value, ts.measured_at
from ems.telemetry_loxone_sensor ts
where ts.sensor_id = pp.water_temp_sensor_id
and ts.measured_at > now() - interval '24 hours'
order by ts.measured_at desc
limit 1
) t on true
where pp.id = p_pump_id;
$fn$;
comment on function ems.fn_pool_daily_runtime_min is
'Cíl minut filtrace/den: clamp(base + per_c×(teplotaref), min, max) z poslední teploty vody (<24 h), jinak daily_runtime_min. JSON s detailem pro UI/solver.';