Skip to content

Repository files navigation

❄️ TCL Air Conditioner Β· ESPHome Controller

Native ESPHome (ESP-IDF) firmware for TCL / Electriq / Daichi split ACs Turn a dumb IR/UART air conditioner into a fully controllable Home Assistant climate entity β€” over UART, no IR blaster required.

UART 9600 8E1 Β· ESP32-C3 / C6 Β· esp-idf Β· WiFi or Thread Β· MIT


πŸ“‘ Table of Contents


✨ Features

Feature
🌑️ Full climate control β€” power, mode, target temperature, fan speed, swing
🏠 Native Home Assistant API β€” no MQTT broker, no cloud, no IR blaster
πŸ”Œ Direct UART β€” talks to the AC's service port at 9600 baud, 8E1
🧱 ESP-IDF framework β€” required for Thread / 802.15.4 support
πŸ“‘ WiFi or Thread β€” pick one per device; Thread needs an ESP32-C6/H2
πŸ“¦ Git-pulled packages β€” your device YAML stays slim; all logic lives in the repo
πŸ–₯️ Optional OLED + status LEDs for TX/RX activity

🧰 What You Need

Recommended boards

Board Radio ~Price Notes
ESP32-C3-DevKitM-1 WiFi ~8 € Cheapest path (WiFi only)
ESP32-C6-DevKitC-1 WiFi + 802.15.4 ~10 € Best for Thread
Seeed XIAO ESP32-C6 WiFi + 802.15.4 ~10 € Tiny, breadboard-friendly

Other parts

  • USB-A plug / cable to reach the AC's service port β€” e.g. AliExpress
  • SSD1306 OLED 128Γ—32 (optional, IΒ²C)
  • 2Γ— 5 mm LEDs + resistors (optional, TX/RX activity)

⚠️ Not every AC model exposes a usable UART service port. Check your unit before buying hardware β€” see Supported Air Conditioners.


πŸ”Œ Wiring

USB-A service plug β†’ ESP32

⚠️ The AC's "USB-A" service port is not real USB β€” it is repurposed as a UART port with a non-standard pinout. The mechanical USB-A pins do not carry their normal signals, so do not treat this as a USB connection. Wire it exactly as below (the authoritative mapping from the sorz2122/tclac project):

USB-A pin Wire color ESP32 pin
GND Black 5V / VIN
D+ Green GND
Dβˆ’ Gray / White RX
VBUS Red TX

πŸ”„ Cross TX↔RX: the AC's Dβˆ’ goes to the ESP's RX, and the AC's VBUS goes to the ESP's TX. The GND and D+ pins are swapped relative to a normal USB cable (mechanical GND carries 5V, mechanical D+ is ground), so double-check with a multimeter before powering up.

Default GPIO assignment (ESP32-C3 sample)

Function GPIO Package
UART TX β†’ AC GPIO1 core.yaml (uart_tx)
UART RX ← AC GPIO3 core.yaml (uart_rx)
IΒ²C SDA (OLED) GPIO0 screen.yaml
IΒ²C SCL (OLED) GPIO2 screen.yaml
RX LED GPIO6 leds.yaml (receive_led)
TX LED GPIO4 leds.yaml (transmit_led)

All UART pins are configurable via the uart_rx / uart_tx substitutions β€” change them to match your board. For example the Seeed XIAO ESP32-C6 uses GPIO16 (RX) / GPIO17 (TX).


πŸš€ Quick Start

You have two ready-made sample configs β€” pick the one matching your connectivity. Both pull everything else (UART, climate, switches, selects, OTA, API) from this repo via packages:.

Config file Connectivity Board
TCL-Conditioner.yaml πŸ“Ά WiFi ESP32-C3 (or any ESP32)
TCL-Conditioner-thread.yaml 🧡 Thread ESP32-C6 / H2 (needs 802.15.4)

Step 1 β€” Clone

git clone https://github.com/steuerlexi/tclac-esphome.git
cd tclac-esphome

Step 2 β€” Create your secrets.yaml

ESPHome reads secrets.yaml from the same folder as your config. Copy the template and fill in your values:

cp secrets.yaml.example secrets.yaml   # then edit secrets.yaml

See πŸ” Secrets below for the required keys.

Step 3 β€” Edit your config

Open TCL-Conditioner.yaml (WiFi) or TCL-Conditioner-thread.yaml (Thread) and set the substitutions:

substitutions:
  device_name: tclac          # unique, lowercase, no spaces
  humanly_name: "TCL AC"      # shown in Home Assistant
  uart_rx: GPIO3              # adjust to your board
  uart_tx: GPIO1

Pick the packages you want (Thread config shown):

packages:
  remote_package:
    url: https://github.com/steuerlexi/tclac-esphome.git
    ref: master
    files:
      - packages/core.yaml          # mandatory β€” UART + climate + API + OTA
      - packages/openthread.yaml    # Thread  (WiFi config: use packages/wifi.yaml)
      # - packages/leds.yaml        # optional TX/RX LEDs
      # - packages/screen.yaml      # optional OLED
    refresh: 30s

Step 4 β€” Flash

pip install esphome                 # or use the ESPHome Dashboard
esphome run TCL-Conditioner.yaml    # or TCL-Conditioner-thread.yaml

πŸ” Secrets (secrets.yaml)

secrets.yaml is git-ignored β€” it never leaves your machine. The committed configs contain only !secret key references, so they're safe to share.

WiFi build (TCL-Conditioner.yaml)

# secrets.yaml
wifi_ssid: "YourWiFi"
wifi_password: "your-wifi-password"
api_key: "BASE64_ESPHOME_API_KEY=="     # generate one: https://esphome.io/components/api.html
ota_pass: "your-ota-password"
recovery_pass: "your-fallback-password"

Thread build (TCL-Conditioner-thread.yaml)

# secrets.yaml
thread_network_name: "TCL-Thread"
thread_channel: "15"
thread_pan_id: "0x1234"
thread_ext_pan_id: "dead00beef00cafe"
thread_network_key: "00112233445566778899aabbccddeeff"
thread_pskc: "00112233445566778899aabbccddeeff"
thread_mesh_local_prefix: "fdde:ad00:beef:0::/64"
api_key: "BASE64_ESPHOME_API_KEY=="
ota_pass: "your-ota-password"
recovery_pass: "your-fallback-password"

πŸ’‘ Get your Thread credentials from your Thread Commissioner / Border Router (e.g. Apple HomePod, Home Assistant Connect ZBT-1). Generate an ESPHome API key at https://esphome.io/components/api.html.


πŸ’Ύ Flashing

First flash (USB)

esphome run TCL-Conditioner.yaml        # pick the right port when prompted

OTA updates (after first flash)

Once the device is on your network (WiFi or Thread), updates go over the air:

esphome run TCL-Conditioner.yaml --device tclac.local
# Thread devices: use the device's Thread/mDNS address

ESPHome Dashboard

Prefer a GUI? Use the ESPHome Dashboard β€” just drop the config folder in and click Install.


🏠 Home Assistant Integration

After flashing, the device shows up automatically:

  1. Settings β†’ Devices & Services in Home Assistant β†’ the tclac device is discovered via the native API.
  2. Click Configure and enter your api_key (it must match secrets.yaml).
  3. A new Climate entity appears β€” add it to a dashboard and you're done. πŸŽ‰

Thread devices need a Thread Border Router (Home Assistant Connect ZBT-1, Apple HomePod, etc.) to be reachable from HA.


πŸŽ›οΈ Entities & Controls

Climate entity (climate.tclac)

Control Options
⏻ Power On / Off
🌑️ Target temp 16 – 31 Β°C (1Β° step)
πŸŒ€ Mode OFF Β· AUTO Β· COOL Β· HEAT Β· DRY Β· FAN_ONLY
πŸ’¨ Fan speed AUTO Β· QUIET Β· LOW Β· MIDDLE Β· MEDIUM Β· HIGH Β· FOCUS Β· DIFFUSE
↔️ Swing OFF Β· VERTICAL Β· HORIZONTAL Β· BOTH
πŸŒ™ Preset NONE Β· ECO Β· SLEEP Β· COMFORT

Config switches & selects

Entity Type What it does
πŸ”” Beeper switch Beep on command confirmation
πŸ–₯️ Display switch Show setpoint on the indoor unit
πŸ’‘ Display on module switch LED indication for AC data exchange
⚑ Force config switch Apply Beeper/Display immediately (sorz2122 behavior gates these on Force)
↕️ Vertical swing select Top↔Bottom Β· Upper half Β· Lower half
↔️ Horizontal swing select Left↔Right Β· Left Β· Center Β· Right
πŸ“Œ Vertical fixing select Last Β· Max up Β· Upper Β· Center Β· Lower Β· Max down
πŸ“Œ Horizontal fixing select Last Β· Max left Β· Left Β· Center Β· Right Β· Max right

πŸ“Œ To park the vertical flap straight ahead when swing is off: set Vertical fixing β†’ In the center.


🌑️ Supported Air Conditioners

Manufacturer Models (confirmed)
TCL TAC-07CHSA, TAC-09CHSA, TAC-12CHSA, TAC-12CHDA, TAC-12CHFA
Daichi AIR20AVQ1, AIR25AVQS1R-1, DA35EVQ1-1
Axioma ASX09H1, ASB09H1
Dantex RK-12SATI, RK-12SATIE
Electriq (various split units sharing the TCL protocol)

Not all units expose the UART service port β€” verify before buying hardware.


🧭 Architecture

  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
  β”‚           Home Assistant             β”‚
  β”‚      (native ESPHome API, TLS)       β”‚
  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                    β”‚  WiFi  or  Thread / 802.15.4
                    β”‚
  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
  β”‚          ESPHome device              β”‚
  β”‚   esp-idf  Β·  ESP32-C3 / C6          β”‚
  β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”    β”‚
  β”‚  β”‚   tclac climate component    β”‚    β”‚
  β”‚  β”‚  (UART protocol Β· 9600 8E1)  β”‚    β”‚
  β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜    β”‚
  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                    β”‚  UART 9600 baud, 8 data, Even parity, 1 stop
                    β”‚
  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
  β”‚      TCL / Electriq / Daichi AC      β”‚
  β”‚       (service / USB-A port)         β”‚
  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

πŸ“¦ Repository Structure

tclac-esphome/
β”œβ”€β”€ components/tclac/          # πŸ”§ The ESPHome climate component (C++ + Python)
β”‚   β”œβ”€β”€ tclac.cpp / tclac.h
β”‚   β”œβ”€β”€ climate.py
β”‚   └── __init__.py
β”œβ”€β”€ packages/                  # πŸ“¦ Shared YAML, pulled by device configs
β”‚   β”œβ”€β”€ core.yaml              #    mandatory: UART, climate, API, OTA, switches, selects
β”‚   β”œβ”€β”€ wifi.yaml              #    optional: WiFi connectivity
β”‚   β”œβ”€β”€ openthread.yaml        #    optional: Thread / 802.15.4 (esp-idf only)
β”‚   β”œβ”€β”€ leds.yaml              #    optional: TX/RX status LEDs
β”‚   └── screen.yaml            #    optional: SSD1306 OLED
β”œβ”€β”€ devices/                   # πŸ–₯️ Real-world device configs (slim, git-pulled)
β”‚   └── tclac-klima.yaml       #    example: XIAO ESP32-C6, Thread, GPIO16/17
β”œβ”€β”€ TCL-Conditioner.yaml       # πŸ“Ά Sample config β€” WiFi
β”œβ”€β”€ TCL-Conditioner-thread.yaml# 🧡 Sample config β€” Thread
└── secrets.yaml.example       # πŸ”‘ Template for your local secrets.yaml (git-ignored)

The package-based approach keeps your device file tiny β€” it holds only identity, hardware pins, connectivity, and secret references. Everything else is downloaded from this repo over git, so updates to the component or packages reach your device on the next compile without you editing your config.


πŸ› οΈ Troubleshooting

❌ No connection to the AC

[TCL] Wrong byte
[TCL] Invalid checksum
  • Check the non-standard USB-A β†’ ESP32 wiring (AC Dβˆ’ β†’ ESP RX, AC VBUS β†’ ESP TX, AC GND β†’ 5V/VIN, AC D+ β†’ GND) β€” see Wiring. This is not a normal USB pinout.
  • Verify UART settings: 9600 baud, 8 data bits, Even parity, 1 stop bit.
  • Confirm your uart_rx / uart_tx pins match the board you actually wired.
  • Check cable continuity on the USB-A service plug.

πŸ“Ά WiFi won't connect

  • Verify wifi_ssid / wifi_password in secrets.yaml.
  • Move the device closer to the router; check signal_strength sensor.
  • The fallback hotspot ({device_name} Fallback Hotspot) appears if WiFi fails β€” connect with recovery_pass to reconfigure.

🧡 Thread device not reachable

  • You need a Thread Border Router on the same network as Home Assistant.
  • Confirm the Thread network credentials in secrets.yaml match your commissioned network.
  • ESP-IDF framework + an 802.15.4-capable board (C6/H2) are required for Thread.

πŸ”„ "My fix didn't take effect after a reflash"

ESPHome caches git-pulled packages and components under .esphome/ and does not always re-fetch on refresh:. After pulling an update, force a clean fetch:

rm -rf .esphome/packages .esphome/external_components
esphome compile your-config.yaml

πŸ“‹ Enable debug logging

logger:
  level: DEBUG
  # baud_rate: 0   # keep 0 β€” logging shares the UART with the AC

πŸ™‹ Credits

This fork builds on the work of several people in the ESPHome / TCL-AC community:

  • Miguel Angel Lopez β€” original ESPHome component
  • xaxexa β€” protocol modifications
  • Nightingale with a soldering iron (Π‘ΠΎΠ»ΠΎΠ²Π΅ΠΉ с паяльником) β€” refactoring & component
  • sorz2122 β€” ESPHome ESP-IDF edition & the authoritative flap-encoding implementation

ESPHome ESP-IDF + package-architecture edition maintained in this repo.

β˜• If this project is useful to you, consider supporting the original author: https://buymeacoffee.com/sorz2122


πŸ“„ License

Licensed under the MIT License β€” see the component headers for details. Feel free to fork, adapt, and use in your own smart home.


Made with ❄️ for the Smart Home community

Contributions welcome β€” fork, branch, PR. See Repository Structure to get started.

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages