defmodule ToweropsWeb.DeviceLive.Components.WirelessTab do @moduledoc """ LiveComponent for the device wireless clients tab. Displays wireless clients connected to the device with optional subscriber matching from Gaiia integration. """ use ToweropsWeb, :live_component alias ToweropsWeb.DeviceLive.Helpers.DataLoaders @impl true def update(assigns, socket) do {:ok, socket |> assign(assigns) |> load_wireless_data()} end defp load_wireless_data(socket) do device_id = socket.assigns.device.id wireless_data = DataLoaders.load_wireless_data(device_id) socket |> assign(:wireless_clients, wireless_data.wireless_clients) |> assign(:wireless_client_subscribers, wireless_data.wireless_client_subscribers) |> assign(:wireless_client_count, wireless_data.wireless_client_count) |> assign(:wireless_matched_count, wireless_data.wireless_matched_count) |> assign(:wireless_last_updated, wireless_data.wireless_last_updated) |> assign(:wireless_clients_available, wireless_data.wireless_clients_available) end @impl true def render(assigns) do ~H"""
<%= if @wireless_clients && length(@wireless_clients) > 0 do %>

{t("Connected Wireless Clients")}

{length(@wireless_clients)} clients — {@wireless_matched_count} subscribers matched <%= if @wireless_last_updated do %> · Last updated {format_relative_time(@wireless_last_updated)} <% end %>

<%= for client <- @wireless_clients do %> <% subscriber_link = Map.get(@wireless_client_subscribers, String.downcase(client.mac_address)) %> <% end %>
MAC Address IP Address Subscriber Signal SNR Distance TX Rate RX Rate Uptime
{client.mac_address} <.icon name="hero-clipboard-document" class="h-3 w-3 inline ml-0.5 opacity-0 group-hover/mac:opacity-100 transition-opacity text-gray-400" /> <%= if client.ip_address do %> {client.ip_address} <.icon name="hero-clipboard-document" class="h-3 w-3 inline ml-0.5 opacity-0 group-hover/ip:opacity-100 transition-opacity text-gray-400" /> <% else %> <% end %> <%= if subscriber_link && subscriber_link.gaiia_account do %> {subscriber_link.gaiia_account.account_name} <% else %> <% end %> <%= if client.signal_strength do %> <.signal_badge value={client.signal_strength} /> <% else %> <% end %> <%= if client.snr do %> <.snr_badge value={client.snr} /> <% else %> <% end %> <%= if client.distance do %> {client.distance}m <% else %> <% end %> {format_rate(client.tx_rate)} {format_rate(client.rx_rate)} <%= if client.uptime_seconds do %> {format_uptime(client.uptime_seconds * 100)} <% else %> <% end %>
<% else %>
<.icon name="hero-signal" class="mx-auto h-12 w-12 text-gray-400 dark:text-gray-600" />

{t("No wireless clients")}

{t("No clients are currently connected to this access point.")}

<% end %>
""" end # Helper functions defp format_relative_time(datetime) do ToweropsWeb.TimeHelpers.format_time_ago(datetime) end defp format_rate(nil), do: "—" defp format_rate(kbps) when is_integer(kbps) do mbps = kbps / 1000 "#{Float.round(mbps, 1)} Mbps" end defp format_uptime(timeticks) when is_integer(timeticks) do seconds = div(timeticks, 100) hours = div(seconds, 3600) remainder = rem(seconds, 3600) minutes = div(remainder, 60) cond do hours > 0 -> "#{hours}h #{minutes}m" minutes > 0 -> "#{minutes}m" true -> "<1m" end end defp format_uptime(_), do: "—" end