FritzApi

Build StatusDocsHex.pm

Fritz!Box Home Automation API Client for Elixir (documentation).

Usage

iex> {:ok, client} = FritzApi.Client.new()
...>                 |> FritzApi.Client.login("admin", "changeme")

iex> FritzApi.get_device_list_infos(client)
{:ok, [%FritzApi.Actor{
  ain: "687690315761",
  fwversion: "03.87",
  id: 21,
  manufacturer: "AVM",
  name: "FRITZ!DECT #1",
  powermeter: %{energy: 0.475, power: 0.0},
  present: true,
  productname: "FRITZ!DECT 200",
  switch: %{
    devicelock: false,
    lock: false,
    mode: :manual,
    state: false
  },
  temperature: %{
    celsius: 23.5,
    offset: 0.0
  }
}]}

iex> FritzApi.set_switch_off(client, "687690315761")
:ok

iex> FritzApi.get_temperature(client, "687690315761")
{:ok, 23.5}

Example: Automatic Session Refresh

defmodule Switch do
  use GenServer

  defmodule State do
    @derive {Inspect, except: [:password]}
    defstruct [:username, :password, :ain, :client]
  end

  def start_link(opts) do
    GenServer.start_link(__MODULE__, opts)
  end

  def turn_on, do: GenServer.call(__MODULE__, :turn_on)
  def turn_off, do: GenServer.call(__MODULE__, :turn_off)

  @impl true
  def init(opts) do
    client =
      FritzApi.Client.new(
        base_url: opts[:host],
        receive_timeout: :timer.seconds(30)
      )

    state = %State{
      username: Keyword.fetch!(opts, :username),
      password: Keyword.fetch!(opts, :password),
      ain: Keyword.fetch!(opts, :ain),
      client: client
    }

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

  @impl true
  def handle_continue(:login, %State{} = state) do
    {:ok, client} = FritzApi.Client.login(state.client, state.username, state.password)
    {:noreply, %State{state | client: client}}
  end

  @impl true
  def handle_call(:turn_on, _from, %State{} = state) do
    {result, state} = refresh_session(&FritzApi.set_switch_on(&1, state.ain), state)
    {:reply, result, state}
  end

  def handle_call(:turn_off, _from, %State{} = state) do
    {result, state} = refresh_session(&FritzApi.set_switch_off(&1, state.ain), state)
    {:reply, result, state}
  end


  defp refresh_session(fun, %State{} = state) do
    with {:error, %FritzApi.Error{reason: :session_expired}} <- fun.(state.client),
         {:ok, client} <- FritzApi.Client.login(state.client, state.username, state.password) do
      {fun.(client), %State{state | client: client}}
    else
      result -> {result, state}
    end
  end
end
iex> {:ok, pid} = Switch.start_link(username: "admin", password: "admin", ain: "000111222333")
iex> Switch.turn_on(pid)
:ok

Installation

Add fritz_api to your list of dependencies in mix.exs:

def deps do
  [
    {:fritz_api, "~> 2.0"},
    {:hackney, "~> 1.17"}
  ]
end

By default, fritz_api uses hackney (via Tesla.Adapter.Hackney). Add hackney to the list of dependencies too if you don't want to use another HTTP adapter (see Tesla Adapters to find all available adapters and FritzApi.Client.new/1 on how to configure another adapter).

The docs can be found at hexdocs.pm/fritz_api.

References