lib/ggsv2.ex

defmodule GGSV2 do
  use GenServer

  require Logger

  alias GGSV2.Comm

  @default_polling_interval_ms 10_000

  def start_link(init_arg \\ []) do
    options = Keyword.take(init_arg, [:name])
    GenServer.start_link(__MODULE__, init_arg, options)
  end

  def measure(pid) do
    GenServer.call(pid, :measure)
  end

  @impl true
  def init(options \\ []) do
    # Sample options keyword list :
    # [polling_interval: 10_000]
    polling_interval = Keyword.get(options, :polling_interval, @default_polling_interval_ms)
    :timer.send_interval(polling_interval, :measure)

    Logger.info("Starting GGSV2 : Polling interval : #{polling_interval}")

    state = %{
      init_done: false,
      options: options,
      i2c: nil,
      device_addr: nil,
      last_reading: nil
    }

    {:ok, state}
  end

  defp deferred_init(%{init_done: false, options: options} = state) do
    # Having a deferred init can help main application using this sensor
    # to start event if the sensor has been removed.

    {bus_name, device_addr} = Keyword.get(options, :transport, Comm.discover())

    Logger.info("Starting (defered) GGSV2 on bus: #{bus_name}, address: #{device_addr} : ")

    i2c = Comm.open(bus_name)

    GGSV2.PREHEAT.activate(i2c, device_addr)

    %{
      state
      | init_done: true,
        i2c: i2c,
        device_addr: device_addr
    }
  end

  defp deferred_init(state) do
    state
  end

  @impl true
  def handle_info(
        :measure,
        state
      ) do
    state = deferred_init(state)
    %{i2c: i2c, device_addr: device_addr} = state

    {:noreply,
     %{
       state
       | last_reading: GGSV2.GAS.read_all(i2c, device_addr)
     }}
  end

  @impl true
  def handle_call(:measure, _from, state) do
    {:reply, {:ok, state.last_reading}, state}
  end
end