Espex.InfraredProxy behaviour (espex v0.1.1)

Copy Markdown View Source

Behaviour for infrared proxy adapters.

Implement this module to expose IR transmitters and receivers as ESPHome entities. Home Assistant treats each entity as a remote control — you can send raw-timing transmit commands and receive learned codes through the UI.

IR entities are first-class ESPHome entities, distinct from the Espex.EntityProvider mechanism. They share the same physical connection but are advertised separately.

Callbacks

CallbackPurpose
list_entities/0Advertise the IR devices
transmit_raw/3Transmit a raw-timing pattern
subscribe/1Register a connection handler for receive events
unsubscribe/1Deregister (idempotent)

Entities

list_entities/0 returns a list of Espex.InfraredProxy.Entity structs. Each entity carries:

  • key — stable non_neg_integer(); this is the id you'll see in transmit_raw/3 and the one you emit in receive events
  • object_id — stable string id
  • name — display name
  • capabilities[:transmit], [:receive], or both — declares what the entity can do

Use the capabilities list to model read-only IR blasters (transmit only), learning remotes (receive only), or combined TX+RX devices.

Data flow

Transmit

When the client sends a transmit command, espex calls transmit_raw/3 with:

  • key — the entity id the command is targeting
  • timings — list of pulse durations in microseconds. Positive values are ON periods, negative are OFF (standard IR raw-timing convention)
  • opts — a keyword list matching transmit_opts/0

transmit_opts/0:

KeyDefaultNotes
:carrier_frequency38_000Hz; 38 kHz is the de-facto standard
:repeat_count1how many times to emit the pattern

Receive

When IR is detected, deliver {:espex_ir_receive, key, timings} to every pid that has subscribed via subscribe/1. key is the entity key of the receiving device; timings is the captured pulse pattern in microseconds.

Example

defmodule MyApp.IRAdapter do
  @behaviour Espex.InfraredProxy

  alias Espex.InfraredProxy.Entity

  @key 42

  @impl true
  def list_entities do
    [
      %Entity{
        key: @key,
        object_id: "ir_blaster",
        name: "IR Blaster",
        icon: "mdi:remote",
        capabilities: [:transmit, :receive]
      }
    ]
  end

  @impl true
  def transmit_raw(@key, timings, opts) do
    MyApp.IRHardware.transmit(timings,
      carrier_hz: opts[:carrier_frequency],
      repeat: opts[:repeat_count]
    )
  end

  def transmit_raw(_unknown_key, _timings, _opts), do: {:error, :no_such_entity}

  @impl true
  def subscribe(pid), do: MyApp.IRSubscribers.add(pid)

  @impl true
  def unsubscribe(pid), do: MyApp.IRSubscribers.remove(pid)
end

And in your hardware read loop, whenever IR is decoded:

Enum.each(MyApp.IRSubscribers.all(), fn pid ->
  send(pid, {:espex_ir_receive, @key, timings})
end)

Wiring

Espex.start_link(
  device_config: [name: "ir-bridge"],
  infrared_proxy: MyApp.IRAdapter
)

Summary

Callbacks

Return the list of IR entities this adapter exposes.

Subscribe the given pid to infrared receive events. Idempotent.

Transmit a raw timing pattern on the device identified by key.

Unsubscribe a previously subscribed pid. Idempotent.

Types

transmit_opts()

@type transmit_opts() :: [
  carrier_frequency: non_neg_integer(),
  repeat_count: pos_integer()
]

Options for transmit_raw/3:

  • :carrier_frequency — carrier frequency in Hz (defaults to 38_000)
  • :repeat_count — number of repeats (defaults to 1)

Callbacks

list_entities()

@callback list_entities() :: [Espex.InfraredProxy.Entity.t()]

Return the list of IR entities this adapter exposes.

subscribe(subscriber)

@callback subscribe(subscriber :: pid()) :: :ok

Subscribe the given pid to infrared receive events. Idempotent.

transmit_raw(key, timings, transmit_opts)

@callback transmit_raw(key :: non_neg_integer(), timings :: [integer()], transmit_opts()) ::
  :ok | {:error, term()}

Transmit a raw timing pattern on the device identified by key.

unsubscribe(subscriber)

@callback unsubscribe(subscriber :: pid()) :: :ok

Unsubscribe a previously subscribed pid. Idempotent.