defmodule MicrowavepropWeb.PskrSpotsLive do @moduledoc "`/pskreporter` — recent PSK Reporter spot aggregates. Refreshes every 60s." use MicrowavepropWeb, :live_view import Ecto.Query alias Microwaveprop.Format alias Microwaveprop.Pskr.SpotHourly alias Microwaveprop.Repo @limit 100 @refresh_ms 60_000 @impl true def mount(_params, _session, socket) do _ = if connected?(socket), do: schedule_refresh() spots = fetch_recent_spots() {:ok, socket |> assign( page_title: "PSK Reporter Spots", limit: @limit, total_spots: fetch_total_spots(), band_counts: fetch_band_counts() ) |> stream(:spots, spots, reset: true)} end @impl true def handle_info(:refresh_spots, socket) do _ = schedule_refresh() spots = fetch_recent_spots() {:noreply, socket |> assign( total_spots: fetch_total_spots(), band_counts: fetch_band_counts() ) |> stream(:spots, spots, reset: true)} end defp schedule_refresh do Process.send_after(self(), :refresh_spots, @refresh_ms) end defp fetch_recent_spots do SpotHourly |> order_by(desc: :last_spot_at) |> limit(@limit) |> Repo.all() end defp fetch_total_spots do Repo.one(from(s in SpotHourly, select: coalesce(sum(s.spot_count), 0))) end defp fetch_band_counts do Repo.all( from(s in SpotHourly, group_by: s.band, select: {s.band, sum(s.spot_count)}, order_by: [desc: sum(s.spot_count)] ) ) end defp fmt_dt(nil), do: "—" defp fmt_dt(dt), do: Calendar.strftime(dt, "%Y-%m-%d %H:%M UTC") defp fmt_snr(min, max) when min == max, do: "#{min} dB" defp fmt_snr(min, max), do: "#{min}–#{max} dB" defp fmt_callsigns([]), do: "—" defp fmt_callsigns(calls), do: Enum.join(calls, ", ") @impl true def render(assigns) do ~H""" <.header> PSK Reporter Spots <:subtitle>Last {@limit} spot aggregates from the MQTT firehose
Total spots stored: {Format.number(@total_spots)} {band} {Format.number(count)}
No spots yet
Last Heard (UTC) Band From From Grid To To Grid Distance Spots SNR Modes
{fmt_dt(spot.last_spot_at)} {spot.band} {fmt_callsigns(spot.sender_callsigns)} {spot.sender_grid} {fmt_callsigns(spot.receiver_callsigns)} {spot.receiver_grid} {Format.distance_km(spot.distance_km)} {Format.number(spot.spot_count)} {fmt_snr(spot.min_snr_db, spot.max_snr_db)} {mode}

No PSK Reporter spots received yet. The MQTT listener is running — spots will appear here as they arrive.

""" end end