140 lines
5.3 KiB
SQL
140 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()
|
||
-- Python notifikace používají jen ceny v horizontu 6 h (24×15 min slotů).
|
||
and v.interval_start < now() + interval '6 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).';
|