Powered by AppSignal & Oban Pro

Caveatica Socket Client

livebooks/fake_caveatica.livemd

Caveatica Socket Client

Mix.install([
  {:slipstream, "~> 1.1"},
  {:kino, "~> 0.12.0"},
  {:jason, "~> 1.4"}
])

Section

client_config = {Caveatica.SocketClient, uri: "ws://192.168.0.7:4000/control/websocket"}
defmodule Caveatica.SocketClient do
  @moduledoc """
  Simulate Caveatica.
  """

  use Slipstream, restart: :temporary

  require Logger

  @topic "control"
  @status_interval _half_a_second = 500

  def start_link(args) do
    Slipstream.start_link(__MODULE__, args, name: __MODULE__)
  end

  @impl Slipstream
  def init(config) do
    Logger.info("info: #{inspect(config)}")

    state =
      config
      |> connect!()
      |> assign(:status_timer, nil)
      |> assign(:light, "off")

    {:ok, state, {:continue, :start_ping}}
  end

  @impl Slipstream
  def handle_continue(:start_ping, socket) do
    {:ok, status_timer} = :timer.send_interval(@status_interval, self(), :send_status)

    {:noreply, assign(socket, :status_timer, status_timer)}
  end

  @impl Slipstream
  def handle_connect(socket) do
    Logger.info("handle_connect: calling join/2")
    {:ok, join(socket, @topic, %{a: 1})}
  end

  @impl Slipstream
  def handle_info(:send_status, socket) do
    light_status = socket.assigns.light
    {:ok, _ref} = push(socket, @topic, "status", %{light: light_status})

    {:noreply, socket}
  end

  @impl Slipstream
  def handle_message(@topic, "close", %{"duration" => duration}, socket) do
    Logger.info("close: #{duration}")

    {:ok, socket}
  end

  def handle_message(@topic, "open", %{"duration" => duration}, socket) do
    Logger.info("open: #{duration}")

    {:ok, socket}
  end

  def handle_message(@topic, "light", %{"state" => state}, socket) do
    Logger.info("setting light: #{state}")

    {:ok, assign(socket, :light, state)}
  end

  def handle_message(@topic, event, message, socket) do
    Logger.error("Unexpected push from server: #{event} #{inspect(message)}")

    {:ok, socket}
  end

  @impl Slipstream
  def handle_disconnect(_reason, socket) do
    status_timer = socket.assigns[:status_timer]

    socket =
      if status_timer do
        :timer.cancel(status_timer)
        assign(socket, :status_timer, nil)
      else
        socket
      end

    {:stop, :normal, socket}
  end
end
{:ok, conn} = Kino.start_child(client_config)