lib/sht4x.ex

defmodule SHT4X do
  @moduledoc """
  Use Sensirion SHT4X humidity and temperature sensors in Elixir
  """

  use GenServer

  require Logger

  @type options() :: [GenServer.option() | {:bus_name, bus_name}]

  @typedoc """
  Which I2C bus to use (defaults to `"i2c-1"`)
  """
  @type bus_name :: binary

  @default_bus_name "i2c-1"
  @bus_address 0x44

  @spec start_link(options()) :: GenServer.on_start()
  def start_link(init_arg \\ []) do
    gen_server_opts =
      Keyword.take(init_arg, [:name, :debug, :timeout, :spawn_opt, :hibernate_after])

    GenServer.start_link(__MODULE__, init_arg, gen_server_opts)
  end

  @spec measure(GenServer.server(), Keyword.t()) :: {:ok, SHT4X.Measurement.t()} | {:error, any}
  def measure(server, opts \\ []) do
    GenServer.call(server, {:measure, opts})
  end

  @impl GenServer
  def init(init_arg) do
    bus_name = init_arg[:bus_name] || @default_bus_name
    bus_address = @bus_address

    Logger.info(
      "[SHT4X] Starting on bus #{bus_name} at address #{inspect(bus_address, base: :hex)}"
    )

    transport = SHT4X.Transport.new(bus_name, bus_address)

    case SHT4X.Comm.serial_number(transport) do
      {:ok, serial_number} ->
        Logger.info("[SHT4X] Initializing sensor #{serial_number}")

        state = %{
          serial_number: serial_number,
          transport: transport
        }

        {:ok, state}

      _error ->
        {:stop, "Error connecting to the sensor"}
    end
  end

  @impl GenServer
  def handle_call({:measure, opts}, _from, state) do
    {:ok, data} = SHT4X.Comm.measure(state.transport, opts)
    {:reply, {:ok, SHT4X.Measurement.from_raw(data)}, state}
  end
end