Skip to content
 
 

Latest commit

 

History

292 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 

Repository files navigation

ESPHome component for AC manufactured by Hisense (RS-485 interface) Replacement of the AEH-W4G1 module and others.

1 11

This custom component provides full climate control for air conditioners manufactured by Hisense and its OEM brands (Ballu, etc.) that use the RS-485 protocol. It has been tested on:

❄️ Hisense CITY DC Inverter AS-13UW4RYRCM04G/04W

❄️ Hisense SILVER CRYSTAL SUPER DC Inverter AS-13UW4RVETG01

❄️ Newtek NT-77HSDC12

❄️ Ballu iGreen Pro DC BSAGI-07HN8, BSAGI-12HN8

❄️ Ballu iGreen Pro DC BSAGI-18HN8_V4

❄️ Ballu Platinum DC BSEI-09HN8_V3

❄️ Hisense Free Match Multi Split 4AMW81U4RJC

and should work with many other models.

The component exposes a standard Home Assistant Climate entity, along with a set of optional sensors and switches to access all advanced features of the AC (turbo, eco, quiet, sleep, swing, LED, iFeel (temperature from any sensor from the home assistant) etc.).

Hardware requirements

  • An ESP32 board
  • An RS-485 transceiver (e.g., MAX485) connected to UART pins
  • Wiring:
    - A (RS-485) → A of transceiver
    - B (RS-485) → B of transceiver
    - Transceiver's RXD to ESP RX pin (e.g., GPIO16)
    - Transceiver's TXD to ESP TX pin (e.g., GPIO17)
    - direct connection (not cross-connection)
    - Power (3.3V or 5V depending on module) and GND

⚠️ The AC uses 5V logic levels on its RS‑485 port. Make sure your transceiver is 3.3V‑tolerant if you power the ESP from 3.3V.

image

Installation

Example configuration

A configuration with all optional sensors and switches:

esp32:
  board: esp32dev
  framework:
    type: arduino

logger:
  level: DEBUG
  baud_rate: 0

uart:
  id: ac_uart
  tx_pin: 17
  rx_pin: 16
  baud_rate: 9600
  stop_bits: 1

external_components:
  - source: github://Druidblack/AC-Hi
    refresh: 30s

climate:
  - platform: ac_hi
    name: "Hisense AC"
    id: hisense_ac
    uart_id: ac_uart
    update_interval: 2s
    enable_presets: true

configuration for the iFeel function (uses mqtt, an external IR transmitter, and any thermometer from home assistant)

mqtt:
  broker: 192.168.1.71
  username: admin
  password: admin

  discovery: false
  discover_ip: false
  discovery_retain: false

  topic_prefix: null

  log_topic: null

  birth_message:
  will_message:
  shutdown_message:

sensor:
  - platform: homeassistant
    id: room_temperature_for_ifeel
    entity_id: sensor.ble_temperature_a4c13836f782 # thermometer from the home assistant
    internal: true

switch:
  - platform: template
    id: hisense_ifeel_enabled
    name: "Hisense iFeel"
    optimistic: true
    restore_mode: RESTORE_DEFAULT_OFF
    turn_on_action:
      - ac_hi.ifeel:
          id: hisense_ac
          temperature: !lambda "return id(room_temperature_for_ifeel).state;"
          enabled: true
    turn_off_action:
      - ac_hi.ifeel:
          id: hisense_ac
          temperature: 0
          enabled: false

interval:
  - interval: 2min
    then:
      - if:
          condition:
            switch.is_on: hisense_ifeel_enabled
          then:
            - ac_hi.ifeel:
                id: hisense_ac
                temperature: !lambda "return id(room_temperature_for_ifeel).state;"
                enabled: true

climate:
  - platform: ac_hi
    name: "Hisense AC"
    id: hisense_ac
    uart_id: ac_uart
    update_interval: 2s
    enable_presets: true

    ifeel_mqtt_topic: hisense_ac_zal/ir/kelon168/tx
    ifeel_mqtt_payload: hex
    ifeel_mqtt_qos: 0
    ifeel_mqtt_retain: false

and the rest of the sensors from the main prime

configuration for the IR transmitter

external_components:
  - source: github://Druidblack/AC-iFeel@main
    components: [ kelon168_mqtt_ir ]
    refresh: 30s

mqtt:
  broker: 192.168.1.71
  username: admin
  password: admin

  discovery: false
  discover_ip: false
  discovery_retain: false
  topic_prefix: null
  log_topic: null
  birth_message:
  will_message:
  shutdown_message:

kelon168_mqtt_ir:
  transmitter_id: ir_tx
  topic: hisense_ac_zal/ir/kelon168/tx
  qos: 0
  send_times: 1

For flashing the native module (AEH-W4G1)

  id: ac_uart
  tx_pin: 20
  rx_pin: 
    number: 21
    inverted: true
  baud_rate: 9600

Entities provided

22 2 3

Climate (climate)

  • Modes: OFF, COOL, HEAT, DRY, FAN_ONLY (auto mode is not supported by the AC, auto mode is implemented by enabling smart mode)
  • Fan speeds: AUTO, QUIET, LOW, MEDIUM, HIGH, TURBO
  • Swing modes: OFF, VERTICAL, HORIZONTAL, BOTH
  • Presets (if enable_presets: true): ECO, BOOST (turbo), SLEEP, QUIET, +8 °C
  • Target temperature range: 16–30°C in steps of 1°C
  • Current temperature is read from the AC and displayed

Sensors (optional)

Most sensors publish raw values received from the AC:

Sensor Description
set_temperature Target temperature setpoint (°C)
room_temperature Current indoor temperature (°C)
outdoor_temperature Outdoor air temperature (°C, signed)
outdoor_condenser_temperature Outdoor condenser temperature (°C, signed)
pipe_temperature Indoor pipe temperature (°C)
wind Raw fan speed code (0–18)
sleep_stage Sleep stage code (0–4)
mode_code AC mode code (0–3)
quiet Quiet mode active (binary)
turbo Turbo mode active (binary)
economy Eco mode active (binary)
swing_up_down Vertical swing active (binary)
swing_left_right Horizontal swing active (binary)
compressor_frequency_set Target compressor frequency (Hz)
compressor_frequency Actual compressor frequency (Hz)
compressor_exhaust_temperature Compressor Exhaust Temperature (°C)
power_status Text sensor showing "ON" or "OFF"

Switches (optional)

  • led_switch: Controls the indoor unit’s LED backlight.
  • command_sound : Command reception sound control.

How it works

The component communicates with the AC via a simple request/response protocol over RS‑485.

  • Polling: Every update_interval (default 1s) a short status query (command 0x66) is sent.
  • Write commands: When you change a setting through Home Assistant, the component accumulates changes for 200 ms (debounce) and then sends a full state write (command 0x65) with a correctly calculated CRC.
  • Convergence logic: While HA has priority, the component continues to enforce the desired state until the AC confirms it (by sending a status frame that matches the desired signature). After convergence, remote changes (e.g., from the IR remote) are again accepted and reflected in HA.
  • CRC validation: All incoming frames are checked for CRC correctness; invalid frames are discarded.
  • Write lock timeout: If the AC does not acknowledge a write within 5 seconds, the lock is released to avoid permanent blocking.

Acknowledgements

We would like to thank the following people for their contributions to reverse engineering the protocol and developing this solution:

  • vins.vins (4pda.to forum post) – for initial protocol research and sharing findings.
  • straga (GitHub) – for providing additional protocol details and wiring diagram.
  • artshevchenko (GitHub) - the source component for ESPHome.
  • adipierro (GitHub - IR component for AC hisense control.

If you have contributed to the reverse engineering effort or improved the component, feel free to add your name here via a pull request!

Protocol specification (reverse engineered)

This section documents the RS‑485 protocol used by Hisense/Ballu ACs. It may be useful for understanding the implementation or for adapting it to other models.

Frame format

All frames start with header 0xF4 0xF5 and end with tail 0xF4 0xFB. The length of the frame is variable and is given by frame[4] + 9 bytes.

Byte offset Description
0 0xF4 (header)
1 0xF5 (header)
2 Unknown (usually 0x00)
3 Unknown (usually 0x40)
4 Declared length L – total frame size = L + 9
5 … 8 Unknown (often 0x00)
9 … 12 Unknown (often 0x01 0xFE 0x01 0x00)
13 Command (0x65 – write, 0x66 – read status, 0x??)
14 … L+4 Payload
L+5 … L+6 CRC (16‑bit sum of bytes 2 … L+4, big‑endian)
L+7 … L+8 Tail 0xF4 0xFB

Status response (command 0x66, byte 13 = 102)

When the AC receives a short query (0x66 frame), it responds with a long status frame (command 102). The payload contains all operational data.

Relevant bytes (0‑based within the frame):

Index Description Encoding / Notes
16 Fan speed Reported values: 0/1/2 = AUTO, 10 = QUIET, 12 = LOW, 14 = MEDIUM, 16 = HIGH
17 Sleep mode Raw value. Decode: (value >> 1) gives sleep stage: 0 = off, 1 = sleep_1, 2 = sleep_2, 4 = sleep_3, 8 = sleep_4
18 Power + Mode Bit 3 = power (1=ON). Upper nibble = mode: 0=FAN_ONLY, 1=HEAT, 2=COOL, 3=DRY
19 Target temperature Direct °C value (16–30)
20 Current indoor temperature °C
21 Pipe temperature °C
32 Swing Bits: bit7 = up/down on, bit6 = left/right on
33 Turbo/Eco flags (RX) bit1 = turbo, bit2 = eco
35 Quiet (RX) bit2 = quiet active
36 LED (RX) bit7 = LED on
37 ... (unused)
42 Compressor target frequency Hz
43 Compressor actual frequency Hz
44 Outdoor air temperature °C (signed int8_t)
45 Outdoor condenser temperature °C (signed int8_t)

Write command (command 0x65)

To change settings, a full state frame is sent. The payload is built from the desired values. The CRC must be calculated over bytes 2 … L+4.

Encoding rules (TX):

Field Encoding
Target temperature `((c & 0x1F) << 1)
Fan speed Base codes: AUTO=1, QUIET=10, LOW=12, MEDIUM=14, HIGH=16. TX value = base + 1
Sleep mode `((code) << 1)
Power low nibble of byte 18: ON = 0b1100, OFF = 0b0100
Mode high nibble of byte 18: FAN_ONLY = 0x10, HEAT = 0x30, COOL = 0x50, DRY = 0x70
Turbo byte 33: ON = 0b1100, OFF = 0b0100 (Turbo overrides Eco)
Eco byte 33: ON = 0b110000, OFF = 0b010000 (combined with Turbo)
Quiet byte 35: ON = 0b110000, OFF = 0b010000
Swing byte 32: UD ON = 0b11000000, OFF = 0b01000000; LR ON = 0b00110000, OFF = 0b00010000; combined by addition
LED byte 36: ON = 0b11000000, OFF = 0b01000000

CRC calculation

CRC is a simple 16‑bit sum (big‑endian) of bytes starting from index 2 up to index L+4 (i.e., excluding header, tail, and the CRC bytes themselves). The sum is stored as two bytes: high byte at offset L+5, low byte at L+6.

Example in C++:

uint16_t crc = 0;
for (int i = 2; i < frame.size() - 4; i++) {
    crc += frame[i];
}
frame[frame.size() - 4] = (crc >> 8) & 0xFF;
frame[frame.size() - 3] = crc & 0xFF;

Troubleshooting

  • No communication: Check wiring, baud rate (9600), and ensure that the RS‑485 transceiver is powered correctly.
  • Wrong outdoor temperature: The value is signed. If you see 244°C, it means –12°C was misinterpreted. This has been fixed in the component.
  • Commands not working: Enable verbose logging (esp_log_level: VERBOSE) and inspect the TX/RX frames. Compare them with the protocol specification.

Contributing

Issues and pull requests are welcome. Please ensure your code follows the ESPHome style and includes appropriate logging.

3D case

JST-SM 2.54mm 4pin

RS485 UART (TTL) photo_2026-04-14_21-47-31 photo_2026-04-14_21-47-30 photo_2026-04-14_21-47-30 (2)

About

ESPHome component for ❄️ AC manufactured by Hisense, Ballu, Newtek (RS-485 interface) Replacement of the AEH-W4G1 module and others.

Topics

Resources

Stars

22 stars

Watchers

3 watching

Forks

Releases

Packages

Contributors

Languages