5.6 KiB · text History 9c0fc16
defmodule LambentExWeb.DevicesLive.Index do
  use LambentExWeb, :live_view
  require Logger

  @pubsub_name LambentEx.PubSub

  use Phoenix.LiveEditableView
  alias LambentEx.LiveEdit

  @impl true
  def mount(_params, _session, socket) do
    Phoenix.PubSub.subscribe(@pubsub_name, "scan-82667777")
    Phoenix.PubSub.subscribe(@pubsub_name, "scan-manual")

    {:ok,
     socket
     |> assign(:devices, %{})
     |> assign(:nonew, true)}
  end

  @impl true
  def handle_params(params, _url, socket) do
    {:noreply, apply_action(socket, socket.assigns.live_action, params)}
  end

  defp re_nil(r) do
    case r do
      "" -> nil
      otherwise -> otherwise
    end
  end

  @impl true
  def handle_info({:devices_pub, devices}, socket) do
    n = Map.merge(socket.assigns.devices, devices)
    {:noreply, socket |> assign(:devices, n)}
  end

  @impl true
  def handle_info({:rename, data}, socket) do
    id = data["id"] |> String.trim_trailing("name") |> String.trim_trailing("namem")
    name = data["data"] |> re_nil

    case socket.assigns[:devices] |> Map.get(id) |> Map.get("type") do
      a when a in ["7777-8266", "7777-flex"] ->
        LambentEx.Scan.ESP8266x7777.rename(:mac, id, name)

      "mqtt-tasmota" ->
        xname = socket.assigns[:devices] |> Map.get(id) |> Map.get("dname")
        LambentEx.MQTT.rename(xname, id, name)

      type ->
        Logger.info("Need handler for this type: #{type}")
        :ok
    end

    {:noreply, socket}
  end

  @impl true
  def handle_info({:reorder, data}, socket) do
    id = data["id"] |> String.trim_trailing("ord") |> String.trim_trailing("ordm")
    tgt = data["ok"]["ord"]

    case socket.assigns[:devices] |> Map.get(id) |> Map.get("type") do
      "7777-8266" ->
        LambentEx.Scan.ESP8266x7777.reorder(:mac, id, tgt)

      "7777-flex" ->
        LambentEx.Scan.ESP8266x7777.reorder(:mac, id, tgt)

      type ->
        Logger.info("Need handler for this type: #{type}")
        :ok
    end

    {:noreply, socket}
  end

  @impl true
  def handle_info({:replace, data}, socket) do
    id = data["id"] |> String.trim_trailing("place") |> String.trim_trailing("placem")
    pl = data["data"] |> re_nil

    case socket.assigns[:devices] |> Map.get(id) |> Map.get("type") do
      "7777-8266" ->
        LambentEx.Scan.ESP8266x7777.replace(:mac, id, pl)

      "7777-flex" ->
        LambentEx.Scan.ESP8266x7777.replace(:mac, id, pl)

      "mqtt-tasmota" ->
        xname = socket.assigns[:devices] |> Map.get(id) |> Map.get("dname")
        LambentEx.MQTT.replace(xname, id, pl)

      type ->
        Logger.info("Need handler for this type: #{type}")
        :ok
    end

    {:noreply, socket}
  end

  @impl true
  def handle_info({:manual_result, ip, status}, socket) do
    socket =
      case status do
        {:lambent, sox} ->
          put_flash(
            socket,
            :info,
            "Found a #{max(sox, 1)}ch device at #{ip}, waiting on its MAC to show up in ARP."
          )

        :not_lambent ->
          put_flash(socket, :error, "Nothing lambent answered at #{ip}.")

        :no_iface ->
          put_flash(socket, :error, "#{ip} is not on any of the scanned interfaces.")
      end

    {:noreply, socket}
  end

  @impl true
  def handle_event("manual", %{"ip" => ip}, socket) do
    ip = String.trim(ip)

    case IP.from_string(ip) do
      {:ok, addr} when tuple_size(addr) == 4 ->
        LambentEx.Scan.ESP8266x7777.manual(ip)
        {:noreply, socket |> put_flash(:info, "Probing #{ip} ...")}

      _otherwise ->
        {:noreply, socket |> put_flash(:error, "#{ip} is not a valid IPv4 address.")}
    end
  end

  @impl true
  def handle_event("poke", %{"id" => id, "dname" => name}, socket) do
    case socket.assigns[:devices] |> Map.get(id) |> Map.get("type") do
      a when a in ["7777-8266", "7777-flex"] ->
        LambentEx.Scan.ESP8266x7777.poke(id)

      "mqtt-tasmota" ->
        LambentEx.MQTT.do_poke(name, id)

      type ->
        Logger.info("Need handler for this type: #{type}")
        :ok
    end

    {:noreply, socket}
  end

  # the plain poke uses the indicator LED when the device has one, this one
  # always lights the strands
  @impl true
  def handle_event("poke_strands", %{"id" => id}, socket) do
    case socket.assigns[:devices] |> Map.get(id) |> Map.get("type") do
      a when a in ["7777-8266", "7777-flex"] ->
        LambentEx.Scan.ESP8266x7777.poke(id, :strands)

      type ->
        Logger.info("Need handler for this type: #{type}")
        :ok
    end

    {:noreply, socket}
  end

  @impl true
  def handle_event("poke", %{"id" => id}, socket) do
    case socket.assigns[:devices] |> Map.get(id) |> Map.get("type") do
      a when a in ["7777-8266", "7777-flex"] ->
        LambentEx.Scan.ESP8266x7777.poke(id)

      type ->
        Logger.info("Need handler for this type: #{type}")
        :ok
    end

    {:noreply, socket}
  end

  defp apply_action(socket, :index, _params) do
    socket
    |> assign(:page_link, "Devices")
    |> assign(:page_title, "Detected Devices")
    |> assign(:source, nil)
  end

  # where a device is reached. mqtt devices are told apart by the type their
  # broker stamps on them, not by a "POWER" key: a multi-channel tasmota only
  # reports POWER1, POWER2, ... and used to take the page down
  defp pip(map) do
    case map do
      %{"ip" => ip} when is_tuple(ip) -> ip |> Tuple.to_list() |> Enum.join(".")
      %{"ip" => ip} when is_binary(ip) -> ip
      %{"type" => "mqtt-" <> _kind, "dname" => broker} -> "MQTT (#{broker})"
      %{"type" => "mqtt-" <> _kind} -> "MQTT"
      _otherwise -> "?"
    end
  end

  defp options() do
    [:rgb, :grb, :rgbww, :rgbnw, :rgbcw, :rgbaw, :rgbxw]
  end
end