Most split air conditioners and many portable units are controlled by an infrared remote. If the unit has no Wi-Fi module, or its Wi-Fi only works through the manufacturer's cloud app, an ESP32 with an IR LED can take over the remote's job. ESPHome has a family of climate platforms that do more than replay recorded buttons: they build the full IR command for a chosen mode, temperature and fan speed, and Home Assistant gets a normal thermostat card for the unit.
What the IR climate platforms do
An AC remote does not send "temperature up" when you press the button. The IRremoteESP8266 FAQ explains that air conditioner remotes tend to send the entire state with each press: desired temperature, mode, fan speed, fan direction, timers and more, plus a checksum. Such a message can be around 128 bits long and is often split into two parts sent back to back. That is why recording individual buttons works poorly for air conditioners. You would need a separate code for every combination.
ESPHome's IR Remote Climate component encodes that state itself. You pick the platform that matches your unit's protocol, and ESPHome creates a climate entity. When you change the mode or temperature in Home Assistant, the ESP32 sends the same kind of frame the original remote would.
The limitation is in the medium. Infrared goes one way, so the component cannot read the unit's actual state. It assumes that the unit is in whatever state was last requested, and that assumed state can be restored after the ESP32 reboots. If someone uses the original remote, or the unit misses a command, Home Assistant shows something that is no longer true. For some platforms an IR receiver fixes part of this, as described further down.
Hardware
You need three things:
- An ESP32 board. ESPHome's docs say the remote transmitter works best on an ESP32 or one of its variants, because most of them have a dedicated RMT peripheral that keeps the signal timing accurate. The ESP32-C2 and ESP32-C61 have no RMT hardware and fall back to a software implementation.
- An IR LED, around 940 nm. The IRremoteESP8266 troubleshooting guide says a 940 nm LED works in most cases, and cheap clear ones are fine. The Tasmota docs warn that common ready-made transmitter modules have a weak LED and a short range, and recommend the TSAL6400 if you build your own.
- An NPN transistor to drive the LED. A GPIO pin cannot supply enough current to make an IR LED bright enough. Both the IRremoteESP8266 troubleshooting guide and its example code are blunt about this: connecting the LED straight to the pin is the most common reason it does not work. The usual circuit puts the LED and a series resistor between the supply and the transistor's collector, the emitter goes to ground, and the GPIO drives the base through a resistor. The project's example uses a 2N3904. Pick the resistor values from the LED's datasheet, especially its pulse current rating. IR LEDs are often driven harder than their continuous rating because each burst lasts only milliseconds.
Optionally, add an IR receiver module (a 38 kHz demodulator such as the KY-022 board used in the Tasmota docs). You need it to capture codes from an unsupported remote, and for some platforms it lets the ESP32 follow what the original remote does.
Placement matters more than people expect. The LED must have a line of sight to the unit's IR window, and the strongest part of the beam comes straight out of the LED's tip. The IRremoteESP8266 guide also notes the opposite problem: a very bright LED placed too close can overload the receiver.
A quick test: point a phone camera at the LED while sending a command. A typical digital camera shows the LED flashing, although the IRremoteESP8266 guide warns that this may not work with an iPhone camera.
Pick the platform for your unit
The climate component's documentation lists native platforms, including coolix, daikin, daikin_arc, daikin_brc, fujitsu_general, gree, hitachi_ac344, hitachi_ac424, climate_ir_lg, midea_ir, mitsubishi, tcl112 (Electrolux, TCL, Fuego), toshiba, whirlpool and zhlt01. The last one is based on the ZH/LT-01 remote, which the docs say is used by many local brands, for example Eurom, Chigo, Tristar and Sumikura.
The brand on the unit does not always tell you the protocol. Many air conditioners are made by a handful of manufacturers and sold under other names. The docs give two examples: midea_ir handles both the Midea and Coolix protocols, and the toshiba platform is known to work with Midea model MAP14HS1TBL. If your brand is not listed, look for the remote's model number, or try the platforms of the likely manufacturer.
Some platforms need more settings. gree requires a model (generic, yan, yaa, yac, yac1fb9, yx1ff or yag). mitsubishi has options for the number of fan speeds and for dry and fan-only modes. toshiba and whirlpool take a model that sets the temperature range.
If no native platform fits, there is heatpumpir, which uses the Arduino-HeatpumpIR library and covers dozens of manufacturers, including Panasonic, Samsung, Sharp, Hisense and Mitsubishi Heavy. The ESPHome docs recommend it only when no native platform works, offer no support for the third-party library, and note that its generic API may not match every unit exactly. Some units have more fan speeds than it can express, for example.
The configuration
A minimal configuration for a unit that uses the Coolix protocol, with the LED driver on GPIO4:
remote_transmitter:
pin: GPIO4
carrier_duty_percent: 50%
climate:
- platform: coolix
name: "Living room AC"
carrier_duty_percent: 50% is the value ESPHome's docs give for IR LEDs. The 100% setting is for 433 MHz radio transmitters, so do not copy it from an RF example.
Change the platform name and add the options your unit needs. For a cool-only unit, turn off the heating mode so it does not show up in Home Assistant:
climate:
- platform: coolix
name: "Bedroom AC"
supports_heat: false
supports_cool and supports_heat both default to true. The heat/cool mode, which most remotes call "auto", is turned on automatically only when both are enabled. If your unit has no usable auto mode, set supports_heat_cool: false. The docs also mention a Mitsubishi quirk: on units that only cool, the off command may not work until you set supports_heat: false.
After you flash the ESP32 and adopt it in Home Assistant, the climate entity appears with the ESPHome device. Point the LED at the unit and change the mode. The air conditioner should react the same way it does to the original remote.
Add a room temperature sensor
On its own, the entity has no idea how warm the room is. The sensor option attaches a temperature reading to it, so the thermostat card shows the actual room temperature next to the setpoint. A humidity_sensor option works the same way for humidity.
i2c:
sda: GPIO21
scl: GPIO22
sensor:
- platform: bme280_i2c
temperature:
name: "Living room temperature"
id: room_temperature
humidity:
name: "Living room humidity"
id: room_humidity
climate:
- platform: coolix
name: "Living room AC"
sensor: room_temperature
humidity_sensor: room_humidity
The documentation is clear that this reading is only for display in the frontend. The air conditioner still regulates by its own sensor, which usually sits in the indoor unit near the ceiling. A few platforms go further. With the Toshiba RAC-PT1411HWRU model, and with greeyac in heatpumpir, ESPHome sends the sensor reading to the unit the way the original remote does with its built-in thermometer (Toshiba calls this "Comfort Sense", Gree "I-Feel"). The unit then regulates by the temperature where the ESP32 is. The docs set limits for this: the update_interval of the sensor must be under seven minutes for the Toshiba and under ten minutes for Gree, or the unit goes back to its own sensor. They suggest 30 seconds and 2 minutes respectively.
Track the original remote with a receiver
The table in the docs marks which platforms support a receiver. Most native ones do, but gree, yashima and heatpumpir do not. With a supported platform, an IR receiver placed close to the unit's own IR window lets the ESP32 decode commands from the original remote and update the climate entity.
remote_receiver:
id: ir_rx
pin:
number: GPIO5
inverted: true
mode:
input: true
pullup: true
tolerance: 55%
climate:
- platform: coolix
name: "Living room AC"
sensor: room_temperature
receiver_id: ir_rx
The inverted: true and pull-up settings, and the 55% tolerance, come from the documentation's example, which says the higher tolerance is recommended for some remotes. This still does not tell you what the unit is really doing. The ESP32 hears the remote, not the air conditioner, so a command the unit missed stays invisible.
When your unit is not on the list
If neither a native platform nor heatpumpir works, you can still send recorded commands. ESPHome's guide to IR devices describes the process: connect the receiver, set dump: all, and watch the log while you press buttons on the remote.
remote_receiver:
pin: GPIO5
dump: all
A known protocol shows up decoded, and an unknown one as a list of raw timings. Each captured code can then be sent from a template button with an action such as remote_transmitter.transmit_raw, with carrier_frequency: 38kHz for IR.
For an air conditioner this gives you scenes, not a thermostat. Because each frame carries the full state, one button means one fixed combination, for example "cool, 24 degrees, auto fan" and "off". For many people two or three such buttons are enough to automate cooling from Home Assistant. If you need the full climate entity, check the HeatpumpIR library and ESPHome's feature requests for your model.
Automating it in Home Assistant
The entity behaves like any other climate entity, so you can use climate.set_hvac_mode and climate.set_temperature in automations. Two things are specific to IR control.
First, Home Assistant cannot confirm that a command arrived. For important automations, such as turning cooling off when everyone leaves, it helps to send the command again a minute later, or to check the effect with a temperature sensor or a smart plug measuring the unit's power.
Second, the assumed state goes out of sync whenever someone uses the original remote on a platform without a receiver. If the household uses both, a receiver or a habit of using one of them is the real fix.
Common mistakes
- LED connected straight to the GPIO. The range shrinks to a few centimetres or nothing works at all. Use a transistor.
- LED in the wrong direction. An LED connected backwards does not light up. Check the polarity first.
carrier_duty_percent: 100%copied from an RF example. For IR it should be 50%.- Wrong platform for the brand. If the unit ignores commands, try the platforms of the likely original manufacturer and the
heatpumpirprotocols before you suspect the hardware. - Expecting the room sensor to change how the unit regulates. On most platforms it is display only.
- Recording single buttons from an AC remote and expecting them to work like a TV remote. Each AC frame carries the whole state, so a captured "temperature up" press replays one fixed setting.