defmodule LambentExWeb.DevicesChannel do
@moduledoc """
Mirrors `LambentExWeb.DevicesLive.Index`: the discovered device map, plus
rename / place / order / poke / probe.
Pushes
* `"devices"` — `%{devices: %{mac => device}}`, a snapshot on join and a
delta on every publish. Merge them by mac, the way the liveview does.
* `"probe_result"` — `%{ip: ip, status: "lambent" | "not_lambent" | "no_iface", sox: n}`
* `"interfaces"` — `[%{name:, addr:, enabled:}]`, which interfaces the
sweep runs on, on join and whenever one is turned off or back on.
"""
use Phoenix.Channel
require Logger
alias LambentEx.Scan.ESP8266x7777, as: S
alias LambentExWeb.Channels.Payload
@pubsub LambentEx.PubSub
@impl true
def join("devices", _params, socket) do
Phoenix.PubSub.subscribe(@pubsub, "scan-82667777")
Phoenix.PubSub.subscribe(@pubsub, "scan-manual")
Phoenix.PubSub.subscribe(@pubsub, "scan-ifaces")
send(self(), :snapshot)
{:ok, assign(socket, :devices, %{})}
end
# mqtt devices only exist in the publishing process, so the snapshot covers
# the 7777 ones and the rest land on the next tick
@impl true
def handle_info(:snapshot, socket) do
devices = S.tagged_devices()
push(socket, "devices", %{devices: Payload.devices(devices)})
push(socket, "interfaces", %{interfaces: LambentEx.Scan.NMAP.interfaces()})
{:noreply, assign(socket, :devices, devices)}
end
@impl true
def handle_info({:interfaces, interfaces}, socket) do
push(socket, "interfaces", %{interfaces: interfaces})
{:noreply, socket}
end
@impl true
def handle_info({:devices_pub, devices}, socket) do
push(socket, "devices", %{devices: Payload.devices(devices)})
{:noreply, assign(socket, :devices, Map.merge(socket.assigns.devices, devices))}
end
@impl true
def handle_info({:manual_result, ip, status}, socket) do
payload =
case status do
{:lambent, sox} -> %{ip: ip, status: "lambent", sox: max(sox, 1)}
other -> %{ip: ip, status: to_string(other)}
end
push(socket, "probe_result", payload)
{:noreply, socket}
end
@impl true
def handle_in("rename", %{"id" => id} = params, socket) do
name = params |> Map.get("name") |> blank_to_nil()
case type_of(socket, id) do
t when t in ["7777-8266", "7777-flex"] -> S.rename(:mac, id, name)
"mqtt-tasmota" -> LambentEx.MQTT.rename(dname(socket, id), id, name)
other -> unhandled("rename", other)
end
{:reply, :ok, socket}
end
@impl true
def handle_in("replace", %{"id" => id} = params, socket) do
place = params |> Map.get("place") |> blank_to_nil()
case type_of(socket, id) do
t when t in ["7777-8266", "7777-flex"] -> S.replace(:mac, id, place)
"mqtt-tasmota" -> LambentEx.MQTT.replace(dname(socket, id), id, place)
other -> unhandled("replace", other)
end
{:reply, :ok, socket}
end
@impl true
def handle_in("reorder", %{"id" => id, "ord" => ord}, socket) do
case type_of(socket, id) do
t when t in ["7777-8266", "7777-flex"] -> S.reorder(:mac, id, ord)
other -> unhandled("reorder", other)
end
{:reply, :ok, socket}
end
@impl true
def handle_in("poke", %{"id" => id} = params, socket) do
case type_of(socket, id) do
t when t in ["7777-8266", "7777-flex"] -> S.poke(id, poke_mode(params))
"mqtt-tasmota" -> LambentEx.MQTT.do_poke(dname(socket, id), id)
other -> unhandled("poke", other)
end
{:reply, :ok, socket}
end
# beyond the prefixes the scanner filters out on its own, an interface can be
# told to stay out of the sweep
@impl true
def handle_in("interface", %{"name" => name, "enabled" => enabled}, socket)
when is_boolean(enabled) do
LambentEx.Scan.NMAP.set_enabled(name, enabled)
{:reply, :ok, socket}
end
# ask everything we know about what it is running now, rather than waiting
# for the sweep to come round
@impl true
def handle_in("refresh", _params, socket) do
S.refresh()
{:reply, :ok, socket}
end
@impl true
def handle_in("probe", %{"ip" => ip}, socket) do
ip = String.trim(ip)
case IP.from_string(ip) do
{:ok, addr} when tuple_size(addr) == 4 ->
S.manual(ip)
{:reply, :ok, socket}
_otherwise ->
{:reply, {:error, %{reason: "#{ip} is not a valid ipv4 address"}}, socket}
end
end
defp poke_mode(params) do
case params |> Map.get("mode") do
"indicator" -> :indicator
"strands" -> :strands
_otherwise -> :auto
end
end
defp type_of(socket, id) do
socket.assigns.devices |> Map.get(id, %{}) |> Map.get("type")
end
defp dname(socket, id) do
socket.assigns.devices |> Map.get(id, %{}) |> Map.get("dname")
end
defp blank_to_nil(""), do: nil
defp blank_to_nil(other), do: other
defp unhandled(cmd, type) do
Logger.info("Need handler for #{cmd} on this type: #{type}")
:ok
end
end
neiam
/lambent_ex
Lambent Aether 5
public · Issues · Pulls 1 · Labels · Forks · Compare · Actions success · Badges · Packages
⭐
Log in to mark this repository.
4.9 KiB · text
History
9c0fc16