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
- β¨ Features
- π§° What You Need
- π Wiring
- π Quick Start
- π Secrets (
secrets.yaml) - πΎ Flashing
- π Home Assistant Integration
- ποΈ Entities & Controls
- π‘οΈ Supported Air Conditioners
- π§ Architecture
- π¦ Repository Structure
- π οΈ Troubleshooting
- π Credits
- π License
| 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 |
| 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 |
- 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.
β οΈ 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 thesorz2122/tclacproject):
| 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.
| 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).
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) |
git clone https://github.com/steuerlexi/tclac-esphome.git
cd tclac-esphomeESPHome 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.yamlSee π Secrets below for the required keys.
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: GPIO1Pick 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: 30spip install esphome # or use the ESPHome Dashboard
esphome run TCL-Conditioner.yaml # or TCL-Conditioner-thread.yamlsecrets.yaml is git-ignored β it never leaves your machine.
The committed configs contain only !secret key references, so they're safe to share.
# 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"# 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.
esphome run TCL-Conditioner.yaml # pick the right port when promptedOnce 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 addressPrefer a GUI? Use the ESPHome Dashboard β just drop the config folder in and click Install.
After flashing, the device shows up automatically:
- Settings β Devices & Services in Home Assistant β the
tclacdevice is discovered via the native API. - Click Configure and enter your
api_key(it must matchsecrets.yaml). - 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.
| 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 |
| OFF Β· VERTICAL Β· HORIZONTAL Β· BOTH | |
| π Preset | NONE Β· ECO Β· SLEEP Β· COMFORT |
| 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) |
| select | TopβBottom Β· Upper half Β· Lower half | |
| 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.
| 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.
ββββββββββββββββββββββββββββββββββββββββ
β 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) β
ββββββββββββββββββββββββββββββββββββββββ
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.
[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_txpins match the board you actually wired. - Check cable continuity on the USB-A service plug.
- Verify
wifi_ssid/wifi_passwordinsecrets.yaml. - Move the device closer to the router; check
signal_strengthsensor. - The fallback hotspot (
{device_name} Fallback Hotspot) appears if WiFi fails β connect withrecovery_passto reconfigure.
- You need a Thread Border Router on the same network as Home Assistant.
- Confirm the Thread network credentials in
secrets.yamlmatch your commissioned network. - ESP-IDF framework + an 802.15.4-capable board (C6/H2) are required for Thread.
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.yamllogger:
level: DEBUG
# baud_rate: 0 # keep 0 β logging shares the UART with the ACThis 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
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.