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
| Callback | Purpose |
|---|---|
list_entities/0 | Advertise the IR devices |
transmit_raw/3 | Transmit a raw-timing pattern |
subscribe/1 | Register a connection handler for receive events |
unsubscribe/1 | Deregister (idempotent) |
Entities
list_entities/0 returns a list of Espex.InfraredProxy.Entity
structs. Each entity carries:
key— stablenon_neg_integer(); this is the id you'll see intransmit_raw/3and the one you emit in receive eventsobject_id— stable string idname— display namecapabilities—[: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 targetingtimings— list of pulse durations in microseconds. Positive values are ON periods, negative are OFF (standard IR raw-timing convention)opts— a keyword list matchingtransmit_opts/0
| Key | Default | Notes |
|---|---|---|
:carrier_frequency | 38_000 | Hz; 38 kHz is the de-facto standard |
:repeat_count | 1 | how 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)
endAnd 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
Types
Options for transmit_raw/3
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
@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
@callback list_entities() :: [Espex.InfraredProxy.Entity.t()]
Return the list of IR entities this adapter exposes.
@callback subscribe(subscriber :: pid()) :: :ok
Subscribe the given pid to infrared receive events. Idempotent.
@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.
@callback unsubscribe(subscriber :: pid()) :: :ok
Unsubscribe a previously subscribed pid. Idempotent.