# `Espex.InfraredProxy`
[🔗](https://github.com/bbangert/espex/blob/main/lib/espex/infrared_proxy.ex#L1)

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 |
|----------|---------|
| `c:list_entities/0` | Advertise the IR devices |
| `c:transmit_raw/3` | Transmit a raw-timing pattern |
| `c:subscribe/1` | Register a connection handler for receive events |
| `c:unsubscribe/1` | Deregister (idempotent) |

## Entities

`c: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
    `c: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
`c: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 `t:transmit_opts/0`

`t:transmit_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 `c: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
    )

# `transmit_opts`

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

Options for `c:transmit_raw/3`:

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

# `list_entities`

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

Return the list of IR entities this adapter exposes.

# `subscribe`

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

Subscribe the given pid to infrared receive events. Idempotent.

# `transmit_raw`

```elixir
@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`

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

Unsubscribe a previously subscribed pid. Idempotent.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
