Files
ems/db/routines/R__066_fn_site_notifications_context.sql
2026-04-19 20:15:46 +02:00

139 lines
5.3 KiB
SQL

create or replace function ems.fn_site_notifications_context(p_site_id int)
returns jsonb
language sql
stable
as $fn$
select
case
when not exists (select 1 from ems.site s0 where s0.id = p_site_id) then
jsonb_build_object('error', 'not_found')
else jsonb_build_object(
'timezone',
coalesce(
nullif(trim((select s.timezone from ems.site s where s.id = p_site_id)), ''),
'Europe/Prague'
),
'mode_code',
(select m.mode_code from ems.site_operating_mode m where m.site_id = p_site_id),
'has_plan',
exists (
select 1
from ems.planning_run pr
where pr.site_id = p_site_id
and pr.status = 'active'
),
'tomorrow_slots',
(
select count(*)::int
from ems.vw_site_effective_price v
where v.site_id = p_site_id
and (v.interval_start at time zone coalesce(
nullif(trim((select s2.timezone from ems.site s2 where s2.id = p_site_id)), ''),
'Europe/Prague'
))::date = (
(
current_timestamp at time zone coalesce(
nullif(trim((select s3.timezone from ems.site s3 where s3.id = p_site_id)), ''),
'Europe/Prague'
)
)::date + 1
)
),
'reserve_soc',
(select min(ab.reserve_soc_percent)::float from ems.asset_battery ab where ab.site_id = p_site_id),
'min_soc',
(select min(ab.min_soc_percent)::float from ems.asset_battery ab where ab.site_id = p_site_id),
'soc_pct',
(select li.battery_soc_percent::float from ems.vw_latest_inverter li where li.site_id = p_site_id order by li.measured_at desc nulls last limit 1),
'inv_measured_at',
(select li.measured_at from ems.vw_latest_inverter li where li.site_id = p_site_id order by li.measured_at desc nulls last limit 1),
'hb_last_seen',
(select hb.last_seen from ems.site_heartbeat hb where hb.site_id = p_site_id limit 1),
'price_slots',
coalesce(
(
select jsonb_agg(
jsonb_build_object(
'interval_start', v.interval_start,
'effective_buy_price_czk_kwh', v.effective_buy_price_czk_kwh,
'effective_sell_price_czk_kwh', v.effective_sell_price_czk_kwh
)
order by v.interval_start
)
from ems.vw_site_effective_price v
where v.site_id = p_site_id
and v.interval_start >= now()
and v.interval_start < now() + interval '48 hours'
),
'[]'::jsonb
),
'avg_buy',
(
select avg(v.effective_buy_price_czk_kwh)::float
from ems.vw_site_effective_price v
where v.site_id = p_site_id
and v.interval_start::date in (current_date, current_date + 1)
),
'usable_wh',
(
select coalesce(sum(ab.usable_capacity_wh), 0)::float
from ems.asset_battery ab
join ems.asset_inverter ai on ai.id = ab.inverter_id
where ai.site_id = p_site_id
),
'ev_sessions',
coalesce(
(
select jsonb_agg(
jsonb_build_object(
'id', es.id,
'charger_id', es.charger_id,
'energy_delivered_wh', es.energy_delivered_wh,
'target_soc_pct', es.target_soc_pct,
'session_start', es.session_start,
'soc_at_connect_pct', es.soc_at_connect_pct,
'battery_capacity_kwh', coalesce(av_id.battery_capacity_kwh, av_def.battery_capacity_kwh),
'make', coalesce(av_id.make, av_def.make),
'model', coalesce(av_id.model, av_def.model),
'default_target_soc_pct', coalesce(av_id.default_target_soc_pct, av_def.default_target_soc_pct),
'charger_code', ac.code
)
order by es.id
)
from ems.ev_session es
join ems.asset_ev_charger ac on ac.id = es.charger_id
left join ems.asset_vehicle av_id on av_id.id = es.vehicle_id
left join ems.asset_vehicle av_def
on av_def.default_charger_id = ac.id
and es.vehicle_id is null
where es.site_id = p_site_id
and es.session_end is null
),
'[]'::jsonb
),
'neg_windows',
coalesce(
(
select jsonb_agg(
jsonb_build_object(
'predicted_date', p.predicted_date,
'window_start_hour', p.window_start_hour,
'window_end_hour', p.window_end_hour,
'probability_pct', p.probability_pct
)
order by p.predicted_date, p.window_start_hour
)
from ems.predicted_negative_price_window p
where p.site_id = p_site_id
and p.predicted_date between current_date and current_date + 2
and p.probability_pct >= 50
),
'[]'::jsonb
)
)
end;
$fn$;
comment on function ems.fn_site_notifications_context(int) is
'Vstupy pro build_smart_notifications + infra pravidla (GET /notifications).';