[ Capturing & replaying IR / 433 MHz with ESPHome ]

Half the remotes in a house speak one of two simple dialects: infrared
(TV, AC, media) or 433 MHz (cheap RF outlets, doorbells, blinds). An
ESP32 running ESPHome can listen to both, print the codes, and play them
back — capture-replay with zero firmware coding, just YAML. Point a
remote at it, copy what the log prints into a button, and Home Assistant
can now fire that signal on cue.

Two ESPHome components do the work: remote_receiver to capture and
remote_transmitter to replay. This walks both ends, plus the wiring
and the one big caveat — rolling codes.
Scope. This is for devices you own — your TV, your AC, your own
433 MHz outlets. Rolling-code remotes (modern garage doors, car key
fobs, KeeLoq with a rotating counter) change their code every press
specifically so a captured one is already dead on replay. That's the
whole point of rolling codes; this guide doesn't defeat them and you
shouldn't try to on anything that isn't yours.

[ What you need ]

An ESP32 (or ESP8266) running ESPHome — see
    getting started with the ESP32 if it's your first board.
  — To capture IR: a demodulating IR receiver — a TSOP38238 or
    similar 38 kHz module (VCC, GND, OUT). Grab one from
    Adafruit, DigiKey, or SparkFun.
  — To capture 433 MHz: a 433 MHz receiver module (the little
    superheterodyne RX boards are far better than the cheap ones).
  — To replay IR: an IR LED — drive it through an NPN transistor,
    not straight off a GPIO, if you want range.
  — To replay 433 MHz: a 433 MHz transmitter module (VCC, GND, DATA).

You can wire all four to one ESP32 and have a single capture-and-replay
bridge, which is what the full config below does.

[ Capture — remote_receiver ]

Wire the receiver's data pin to a GPIO and declare it. IR modules pull
the line low on a mark, so IR pins usually want inverted: true and a
pull-up:

  remote_receiver:
    - id: ir_rx
      pin:
        number: GPIO14
        inverted: true
        mode:
          input: true
          pullup: true
      dump: all           # decode every known protocol, plus raw
      tolerance: 25%
      filter: 50us
      idle: 10ms

dump: all is the magic — it tries every decoder ESPHome knows (NEC,
Sony, Samsung, LG, Panasonic, RC5/6, rc_switch, and 30-odd more) and
also prints the raw pulse list. Set the logger to DEBUG, run
esphome logs your.yaml, and press a button on the remote. You'll see
something like:

  [remote.nec] Received NEC: address=0x20DF, command=0x10EF
  [remote.raw] Received Raw: 9000, -4500, 560, -560, 560, -1690, ...

If a named protocol decodes, copy the decoded line (address / command)
— it's compact and robust. If nothing decodes (common for oddball 433
gear), copy the raw pulse list instead. Either transmits fine.

For 433 MHz, add a second receiver — most cheap remotes are
rc_switch or raw, and the noisy RX floor wants a looser tolerance and a
bigger filter:

  remote_receiver:
    - id: rf_rx
      pin: GPIO13
      dump: rc_switch
      tolerance: 60%
      filter: 250us
      idle: 4ms

[ Replay — remote_transmitter ]

The transmitter side is one block per emitter. The only knob that
matters is carrier_duty_percent: IR rides a ~38 kHz carrier
(50%), while 433 MHz is on/off keyed with no carrier (100%):

  remote_transmitter:
    - id: ir_tx
      pin: GPIO4
      carrier_duty_percent: 50%     # IR ~38 kHz
    - id: rf_tx
      pin: GPIO12
      carrier_duty_percent: 100%    # 433 MHz, no carrier

Then drop the code you captured into a template button. Use the
decoded form when you have it, raw otherwise:

  button:
    - platform: template
      name: "TV Power (IR)"
      on_press:
        - remote_transmitter.transmit_nec:
            transmitter_id: ir_tx
            address: 0x20DF
            command: 0x10EF
    - platform: template
      name: "Outlet A On (433)"
      on_press:
        - remote_transmitter.transmit_raw:
            transmitter_id: rf_tx
            code: [320, -960, 320, -960, 960, -320]

There's a transmit_* action per protocol (transmit_sony,
transmit_samsung, transmit_rc_switch_raw, and so on), plus a
repeat: with times / wait_time for gear that wants a signal sent
two or three times to latch.

[ Into Home Assistant ]

Because the buttons live behind ESPHome's api:, Home Assistant
discovers them automatically — each becomes a button entity you can put
on a dashboard, drop into an automation, or trigger by voice. That's the
whole payoff: a $5 IR LED and a 433 transmitter turn a pile of dumb
remotes into HA-controllable devices, no cloud, no bridge, no vendor app.

The ESP32 Bluetooth proxy build is the same idea for BLE; the two
pair well on one board if you have the GPIOs.

[ What won't (and shouldn't) replay ]

Capture-replay only works on fixed-code devices — the signal is the
same every press. That covers most IR, and a lot of cheap 433 outlets,
doorbells, and blinds.

It does not work on rolling-code devices — modern garage doors,
car key fobs, and anything using KeeLoq with a rotating counter. Each
press emits a fresh code derived from a secret + counter, so the one you
captured is already spent. This is a security feature working as
designed. Don't point this at hardware that isn't yours, and don't read
this as a way around a rolling-code system — it isn't one.

[ Full config ]

A complete single-board bridge — both receivers, both transmitters, and
two example buttons — wired to the GPIOs from the sections above. Adjust
pins and paste your own captured codes:

[ See Also ]