lib/hue_sdk/discovery/nupnp.ex

defmodule HueSDK.Discovery.NUPNP do
  @moduledoc """
  N-UPnP discovery of the Hue Bridge.

  Requires a Hue Bridge registered with the official Philips Hue Discovery Portal.

  See `HueSDK.Discovery` for available options.
  """

  alias HueSDK.{Bridge, Config, Discovery, HTTP}

  require Logger

  @behaviour Discovery

  @impl true
  def do_discovery(opts) do
    devices =
      poll_for_discovery(
        Config.portal_host(),
        opts[:max_attempts],
        opts[:sleep]
      )

    {:nupnp, Enum.map(devices, &to_bridge/1)}
  end

  defp poll_for_discovery(portal_host, max_attempts, sleep) do
    poll_for_discovery(portal_host, max_attempts, sleep, 1)
  end

  defp poll_for_discovery(portal_host, max_attempts, sleep, attempt_no)
       when attempt_no <= max_attempts do
    Logger.debug("[#{__MODULE__}] discovery attempt #{attempt_no}/#{max_attempts}..")

    case HTTP.request(:get, portal_host, [], nil, &Jason.decode!/1) do
      {:ok, devices} when is_list(devices) ->
        Logger.debug("[#{__MODULE__}] discovered devices #{inspect(devices)}")
        devices

      _ ->
        :timer.sleep(sleep)
        poll_for_discovery(portal_host, max_attempts, sleep, attempt_no + 1)
    end
  end

  defp poll_for_discovery(_portal_host, max_attempts, _sleep, _attempt_no) do
    Logger.warn("[#{__MODULE__}] exhausted #{max_attempts} discovery attempts!")
    []
  end

  defp to_bridge(device) do
    %Bridge{host: device["internalipaddress"]}
  end
end