Powered by AppSignal & Oban Pro

Homex

livebooks/intro.livemd

Homex

Mix.install([
  {:homex, github: "kevinschweikert/homex"},
  # needed for ESPHome adapter
  {:espex, "~> 0.9.0"},
  # needed for ESPHome autodiscovery
  {:muontrap, "~> 2.0"},
  {:req, "~> 0.6.2"},
  {:kino, "~> 0.19.0"},
])

Homex

Logger.configure(level: :info)
defmodule MySwitch do
  use Homex.Entity.Switch, id: :relay, name: "Pump Relay"

  def handle_on(entity) do
    IO.puts("Relay turned on")
    entity
  end

  def handle_off(entity) do
    IO.puts("Relay turned off")
    entity
  end
end

defmodule MyTemperature do
  use Homex.Entity.Sensor,
    id: :bme280_temp,
    name: "Outside Temperature",
    unit_of_measurement: "°C",
    device_class: "temperature"

  def handle_init(entity) do
    :timer.send_interval(10_000, :measure)
    entity
  end

  def handle_info(:measure, entity) do
    entity |> set_value(Enum.random(-40..40//1))
  end
end

defmodule MyHumidity do
  use Homex.Entity.Sensor,
    id: :bme280_hum,
    name: "Outside Humidity",
    unit_of_measurement: "%",
    device_class: "humidity"

  def handle_init(entity) do
    :timer.send_interval(10_000, :measure)
    entity
  end

  def handle_info(:measure, entity) do
    entity |> set_value(Enum.random(20..90//1))
  end
end

defmodule MyButton do
  use Homex.Entity.Button, id: :gpio17, name: "Open Door"

  def handle_press(entity) do
    IO.puts("my button was pressed")
    entity
  end
end

defmodule MyLight do
  use Homex.Entity.Light, id: :ws2812, name: "Lava Lamp", modes: [:brightness]

  def handle_init(entity) do
    entity |> set_on() |> set_brightness(50)
  end

  def handle_brightness(entity, brightness) do
    IO.puts("Light set to #{brightness}%")
    entity
  end
end

defmodule MyDevice do
  use Homex.Entity.DeviceTrigger, id: :reed_switch, name: "Door Opened"
end

defmodule MyText do
  use Homex.Entity.Text, id: :greeting, name: "Greeting"

  def handle_init(entity), do: set_text(entity, "World")

  def handle_text(entity, text) do
    IO.puts("Hello #{text}")
    entity
  end
end

defmodule MyCamera do
  use Homex.Entity.Camera, id: :ov2640, name: "Front Door Surveillance"

  def handle_init(entity) do
    :timer.send_interval(5_000, :snap)
    entity
  end

  def handle_info(:snap, entity), do: snap(entity)
  def handle_capture(entity), do: snap(entity)

  defp snap(entity) do
    %{body: image} = Req.get!("https://picsum.photos/200", redirect_log_level: false)
    entity |> set_image(image) |> set_attributes(%{bytes: byte_size(image)})
  end
end
{:ok, pid} =
  Homex.start_link(
    id: "livebook",
    devices: [default: [name: "Livebook"]],
    adapters: [{Homex.Adapter.ESPHome, mdns: :system}],
    entities: [
      MySwitch,
      MyTemperature,
      MyHumidity,
      MyButton,
      MyLight,
      MyDevice,
      MyCamera,
      MyText
    ]
  )

Dashboard

Let's have a look at our entities. Each card live updates when you change a value in Homeassistant and vice versa. Try it!

Homex.Livebook.new()

You can get the latest state of an entity with Homex.snapshot/1.

Homex.Entity.snapshot(:bme280_temp)

Adding entities at runtime

Entities don't have to be listed at start time. Homex.add_entity/1 and Homex.add_entities/1 start an entity under the same supervision tree and tell the adapters to republish, so it shows up right away without a restart.

defmodule MyNumber do
  use Homex.Entity.Number, id: :brightness, name: "Brightness", min: 0, max: 100, step: 1

  def handle_init(entity) do
    %{brightness: brightness} = Entity.snapshot(:ws2812)
    set_number(entity, brightness)
  end

  def handle_number(entity, number) do
    Entity.send_command(:ws2812, %{brightness: number})
    entity
  end
end

defmodule MySelect do
  use Homex.Entity.Select, id: :mode, name: "Mode", options: ["on", "off"]

  def handle_init(entity) do
    %{state: state} = Entity.snapshot(:ws2812)
    set_option(entity, if(state, do: "on", else: "off"))
  end

  def handle_option(entity, "on") do
    Entity.send_command(:ws2812, %{state: true})
    entity
  end

  def handle_option(entity, "off") do
    Entity.send_command(:ws2812, %{state: false})
    entity
  end
end

Homex.add_entities([MyNumber, MySelect])