ESPHome integration for Hantech portable AC unit
Find a file
2026-06-27 13:44:11 +02:00
src update readme and add source files 2026-06-27 13:10:07 +02:00
LICENSE Initial commit 2026-06-27 10:50:25 +00:00
README.md update readme 2026-06-27 13:44:11 +02:00

ESPHome integration for Hantech A016-09KR2/A IR Protocol

This write-up documents the process of reverse-engineering the proprietary IR protocol of the Hantech A016-09KR2/A portable air conditioner and integrating it with Home Assistant via ESPHome and an ESP32 IR bridge.

The same unit is sold under several brand names in Europe, including Lifetime Air, Oceanic, and Saneo.


Hardware

  • ESP32 (generic dev board)
  • IR transmitter — 940nm IR LED driven by a NPN transistor

Wiring

Component ESP32 pin
IR LED (via resistor) GPIO14

I used a NPN transistor to drive the IR LED.

Protocol

Parameter Value
Carrier frequency 38 kHz
Mark duration ~485 µs
Short gap (bit = 0) ~612 µs
Long gap (bit = 1) ~1724 µs
Final mark ~539 µs
Bit order MSB first
Header burst None

Frame Layout

Byte  Value    Description
────────────────────────────────────────────────────
B1    0x18     Fixed header (also the checksum target)
B2    varies   Power + mode
B3    varies   Fan speed + sleep flag
B4    varies   Target temperature
B5    varies   Timer hours (normal commands: 0x00)
B6    varies   Timer cancel flag (normal commands: 0x00)
B7    varies   Checksum

Checksum

B7 = (0x18 - (B1 + B2 + B3 + B4 + B5 + B6)) & 0xFF

Equivalently: all 7 bytes always sum to 0x18.

B2 — Power and Mode

Value Meaning
0x01 Power off
0x81 Cool
0x82 Dry (dehumidify)
0x84 Fan only
0xC1 Timer command

B3 — Fan Speed and Sleep

Value Meaning
0x01 Fan low
0x04 Fan high
0x11 Fan low + sleep mode
0x14 Fan high + sleep mode

Sleep mode is encoded as bit 4 of B3: fan_byte |= 0x10.

B4 — Temperature

Temperature is encoded as the decimal value in hex:

°C Hex
16 0x10
17 0x11
18 0x12
30 0x1E
31 0x1F

Range: 1631°C.

Timer Command

The sleep timer sends a separate frame with B2 = 0xC1:

B1 = 0x18
B2 = 0xC1   (timer mode)
B3 = 0x84   (fixed)
B4 = 0x10   (fixed)
B5 = hours  (0x010x18 for 124 hours)
B6 = 0x00
B7 = checksum

Cancelling the timer: there is no dedicated cancel frame. Sending any normal AC state command within ~4 seconds of a timer command cancels the timer. This is the same behaviour as the physical remote.


ESPHome Configuration

Since ESPHome's climate.template platform was removed in recent versions, the integration is split into:

  • ESPHome: exposes select, number, and switch entities
  • Home Assistant: wraps them in a climate entity via HA's template climate platform

ESPHome (hantech-ac.yaml)

The core of the config is a lambda that builds and transmits the 7-byte frame:

remote_transmitter:
  id: ir_tx
  pin: GPIO14
  carrier_duty_percent: 50%

script:
  - id: send_ir
    then:
      - remote_transmitter.transmit_raw:
          transmitter_id: ir_tx
          carrier_frequency: 38000Hz
          code: !lambda |-
            int temp = (int)id(ac_temp).state;
            if (temp < 16) temp = 16;
            if (temp > 31) temp = 31;

            uint8_t fan_byte = (id(ac_fan).state == "high") ? 0x04 : 0x01;
            if (id(ac_sleep).state) fan_byte |= 0x10;

            uint8_t mode_byte;
            std::string mode = id(ac_mode).state;
            if      (mode == "cool")     mode_byte = 0x81;
            else if (mode == "dry")      mode_byte = 0x82;
            else if (mode == "fan_only") mode_byte = 0x84;
            else                         mode_byte = 0x01;

            uint8_t b1=0x18, b2=mode_byte, b3=fan_byte,
                    b4=(uint8_t)temp, b5=0x00, b6=0x00;
            uint8_t b7 = (0x18-(b1+b2+b3+b4+b5+b6)) & 0xFF;
            uint8_t frame[7] = {b1,b2,b3,b4,b5,b6,b7};

            std::vector<int32_t> p;
            p.reserve(7*8*2+1);
            for (int i=0;i<7;i++)
              for (int bit=7;bit>=0;bit--) {
                p.push_back(485);
                p.push_back(-((frame[i]>>bit)&1 ? 1724 : 612));
              }
            p.push_back(539);
            return p;

The full ESPHome config (timer, sleep mode, and HA climate wrapper) is available in this repository.

Files

  • hantech-ac.yaml — ESPHome configuration
  • hantech-template.yaml — Home Assistant template climate configuration, pase this in to your configuration.yaml

References