NOTE: This integration works with LG Outdoor Units (ODUs/inverters). If your ODU doesn't have an LGAP/Central Control interface or integration through a board such as the LG PI-485 board, or you're looking to integrate with the indoor wall panel controller instead, check out: JanM321/esphome-lg-controller.
esphome-lgap is an implementation of the LG Aircon Protocol (LGAP) implemented as an esphome component. This enables you to use an ESP8266/ESP32 to interface directly on the interface generally used for building management system integration/control of LG HVAC units.
One of the best benefits of using this integration is you can use a single LGAP interface on your outdoor unit (ODU) to drive multiple different indoor unit (IDU) zones.
- ✨ Fixed temperature calculation - Corrected current temperature formula to
(192 - raw) / 3for accurate readings - ✨ Immediate temperature updates - Temperatures now appear immediately in Home Assistant on startup (rate-limited thereafter)
- ✨ Pipe temperature sensors - Auto-generated sensors for refrigerant inlet and outlet temperatures
- ✨ Temperature limits enforcement - Heat mode: 16-30°C, other modes: 18-30°C (per LG spec)
- ✨ Error code sensor - Exposes LG service error codes (0 = OK)
- ✨ Zone Active Load - Real-time dynamic load per zone (
nvoLoadEstimate) - ✨ Zone Power State - ON/OFF state flag (
nvoOnOff) - ✨ Zone Design Load - Fixed capacity/duct size index (
nciRatedCapacity) - ✨ ODU Total Load - System-wide compressor load (
nvoThermalLoad)
- ✨ Persistent timer duration - User sets duration once (0-420 minutes), stays saved
- ✨ Auto-start on power ON - Timer automatically starts countdown when AC turns ON
- ✨ Timer remaining sensor - Real-time countdown display
- ✨ Auto-shutoff - AC turns OFF when timer expires, duration stays saved for next use
- ✨ Control Lock - Master child lock (protocol TX4 bit2)
- ✨ Temperature Lock - Prevent temperature changes
- ✨ Fan Speed Lock - Prevent fan speed changes
- ✨ Mode Lock - Prevent mode changes
- ✨ Power Only Mode - Allow only ON/OFF, lock all other controls
- ✨ Wall controller enforcement - Automatically reverts unauthorized changes made at physical wall controller when locks are active
- ✨ Plasma ion control - Air purification switch (protocol TX4 bit4) -
supports_plasma: true - ✨ Auto swing mode - Auto airflow for ducted units -
supports_auto_swing: true - ✨ Auto fan speed - Auto fan mode -
supports_auto_fan: true - ✨ Quiet fan mode - Silent operation -
supports_quiet_fan: true - ✨ Turbo fan mode - Maximum power -
supports_turbo_fan: true
- ✨ Reduced log verbosity - Moved frequent messages to VERBOSE level for cleaner logs
- ✨ Race condition fix - Fixed temperature change race condition with
write_update_pendingflag - ✨ Clean UI by default - All advanced features disabled by default, opt-in per zone
- ✨ Auto-generated entities - All sensors, numbers, and switches auto-created with smart naming
If upgrading from an older version, ensure your YAML includes these base components (required for auto-generated entities):
sensor:
number:
switch:
button:- ✅ Full Climate Control - Native Home Assistant climate entity with standard controls
- ✅ Multi-Zone Support - Control multiple zones from single ODU interface
- ✅ Advanced Sensors - Pipe temperatures, load monitoring, error codes
- ✅ LonWorks Integration - Compatible with LG's commercial BMS protocol fields
- ✅ Control Locks - Child lock, temperature lock, fan lock, mode lock, power-only mode
- ✅ Sleep Timer - Automatic shutoff timer (0-420 minutes)
- ✅ Wall Controller Enforcement - Reverts unauthorized changes made at physical controller when locks active
- ✅ Optional Features - Plasma ion, auto swing, quiet/turbo fan modes (opt-in per zone)
- ✅ Temperature Accuracy - Corrected LG protocol formulas for accurate readings
- ✅ Smart Updates - Configurable temperature update intervals, immediate first reading
- Project background
- LGAP protocol detail
- Build instructions (below)
This whole exercise has been for my own personal learning and experimentation. There are aspects of integrating with high voltage equipment and looking into unpublished protocols. Attempting anything detailed here is at your own risk, may void the warranty of your unit or worse may result in physical harm.
I've tried to build this using the standard esphome external component method and make it as simple as possible to reference in your esphome configuration.
I've tried a couple of ESP32-based devices with varying success. Below is a list of known tested devices.
| Model Number | Price | Link | Test result |
|---|---|---|---|
| M5Stack Atom Lite + ATOMIC RS485 Base | $7.50 USD + $9.95 USD |
https://shop.m5stack.com/products/atom-lite-esp32-development-kit https://shop.m5stack.com/products/atomic-rs485-base |
Recommended |
| Lilygo T-RSC3 | $17.98 USD | https://www.lilygo.cc/products/t-rsc3 | Onboard RS485 interface not compatible |
Refer to the disclaimer above.
On my ODU there are 4 internal pins right next to each other that specify 12V, GND, Central Control A & B. The great thing about the ATOMIC RS485 Base is that you can give it 12V and it will also power the ESP32. That makes the wiring really simple. I wire up each of those pins from the ODU to the pins on the RS485 interface and we're good to go.
For older LG ODUs that don't have onboard CEN_A/CEN_B pins, the PI-485 expansion board adds an LGAP interface. This has been tested and confirmed working (thanks to @tolkachev via PR #15).
PI-485 DIP Switch Settings:
| Switch | Position | Purpose |
|---|---|---|
| 1 | ON | Enable RS485 communication |
| 2 | OFF | — |
| 3 | OFF | — |
| 4 | ON | Enable LGAP protocol mode |
| 5 | ON | — |
| 6-8 | OFF | — |
Setup Steps:
- Set IDU addresses — Each indoor unit must have a unique address set via the wired remote/thermostat. Without this, the PI-485 won't detect the units.
- Check the red LED — On PI-485 boot, the red LED blinks once per detected indoor unit. If it doesn't blink, your IDU addresses aren't set.
- Wire A to A, B to B — Connect the PI-485 RS485 terminals to the ATOMIC RS485 Base. No need to swap A/B. Termination resistors (120 Ohm) are not required.
- Set
tx_byte_0: 0x80in your YAML config (this is the default value in the current code). The PI-485 requires this specific first byte to respond. - Check the green LED — Once ESPHome is running, the green LED should blink indicating RS485 data activity. If it doesn't blink, the PI-485 isn't processing requests.
Confirmed working hardware:
- ODU: FM40AH UH0 (AGUW406FAO) + 4 IDU
- Multi-F systems with PI-485
- M5Stack Atom Lite + ATOMIC RS485 Base
Note: The PI-485 can also be powered from the ODU's 12V pins, same as the direct CEN_A/CEN_B wiring method.
I am making the assumption that you're also using the M5Stack devices as listed above, here is some sample yaml you can add to an existing yaml config.
💡 TIP: Ready-to-use configuration examples:
- Single Zone: lg-single-zone.yaml - One indoor unit
- Multi Zone: lg-multi-zone.yaml - Multiple indoor units (5 zones example)
- General Template: example-config.yaml - Detailed configuration reference
external_components:
- source:
type: git
url: https://github.com/mystardious/esphome-lgap # or jourdant/esphome-lgap
ref: main
components: [ "lgap"]
refresh: 0sec
uart:
id: lgap_uart1
tx_pin:
number: GPIO19
rx_pin:
number: GPIO22
baud_rate: 4800
data_bits: 8
stop_bits: 1
parity: NONE
# Required for auto-generated entities
sensor:
number:
switch:
button:
lgap:
- id: lgap1
uart_id: lgap_uart1
receive_wait_time: 250ms # Response timeout (default: 500ms, increase to 1000ms if unreliable)
loop_wait_time: 100ms # Polling interval (default: 500ms, reduce for faster updates)
tx_byte_0: 0x80 # Frame header byte (default: 0x80)
climate:
- platform: lgap
id: lg_zone_0
name: "Mohamed Ali Room"
lgap_id: lgap1
zone: 5
- platform: lgap
id: lg_zone_1
name: "Cinema Room"
lgap_id: lgap1
zone: 1
- platform: lgap
id: lg_zone_2
name: "Living Room A"
lgap_id: lgap1
zone: 2
- platform: lgap
id: lg_zone_3
name: "Living Room B"
lgap_id: lgap1
zone: 3
- platform: lgap
id: lg_zone_4
name: "Hadi Room"
lgap_id: lgap1
zone: 4Timing Configuration Notes:
- If you experience communication issues or timeouts, try increasing
receive_wait_timeto1000ms - The
loop_wait_timecan be adjusted based on your needs:- Lower values (50-100ms) = faster updates, more protocol traffic
- Higher values (500-1000ms) = slower updates, reduced load
- Most setups work well with the defaults shown above
If you want to add extra zones, you can reference the same lgap_id on the climate component. It is also possible to have multiple LGAP protocol components using different UART components in the same configuration.
The LGAP component supports many advanced features that can be optionally enabled per zone. All features are disabled by default for a clean, minimal interface.
climate:
- platform: lgap
id: lgap_zone_cinema
name: 'Cinema Room'
lgap_id: lgap1
zone: 2
# Optional: Temperature update rate (default: 300000ms / 5 minutes)
temperature_publish_time: 300000ms
# Optional: Enable auto airflow mode for ducted units (default: false)
# Shows "Swing Set: Off or Vertical" in Home Assistant
supports_auto_swing: true
# Optional: Enable auto fan speed mode (default: false)
# Adds "Auto" to fan speed options alongside Low/Medium/High
supports_auto_fan: true
# Optional: Enable quiet/slow fan mode (default: false)
# Adds "Quiet" fan speed option for silent operation
supports_quiet_fan: true
# Optional: Enable turbo/power fan mode (default: false)
# Adds "Focus" fan speed option for maximum cooling/heating
supports_turbo_fan: true
# Optional: Enable plasma ion air purification control (default: false)
# Creates a switch entity to control plasma ion feature
supports_plasma: trueThe component automatically creates the following sensors for each zone:
- Pipe In Temperature - Refrigerant pipe inlet temperature (°C)
- Pipe Out Temperature - Refrigerant pipe outlet temperature (°C)
- Error Code - Service error codes (0 = OK)
- Zone Active Load - Real-time dynamic load index (LonWorks
nvoLoadEstimate) - Zone Power State - Zone on/off state flag (LonWorks
nvoOnOff) - Zone Design Load - Fixed design capacity index (LonWorks
nciRatedCapacity) - ODU Total Load - Total outdoor unit load across all zones (LonWorks
nvoThermalLoad)
Each zone automatically includes:
- Sleep Timer - Number input (0-420 minutes, 0 disables timer)
- Timer Remaining - Countdown sensor showing minutes until auto-shutoff
- Control Lock - Master lock switch (child lock)
- Lock Temperature - Prevent temperature changes
- Lock Fan Speed - Prevent fan speed changes
- Lock Mode - Prevent mode changes
- Power Only Mode - Allow only ON/OFF, lock all other controls
- Plasma - Control plasma ion air purification (only if
supports_plasma: true)
All lock switches enforce restrictions both from Home Assistant and from physical wall controller changes. If a user attempts to change a locked parameter at the wall controller, the system will automatically revert the change.
The component enforces LG protocol temperature limits:
- Heat mode: 16-30°C
- All other modes (Cool/Dry/Fan/Auto): 18-30°C
climate:
- platform: lgap
id: bedroom
name: 'Master Bedroom'
lgap_id: lgap1
zone: 3
supports_quiet_fan: true # Enable quiet mode for night
supports_plasma: true # Enable air purification
- platform: lgap
id: living_room
name: 'Living Room'
lgap_id: lgap1
zone: 4
supports_auto_swing: true # Ducted unit with auto airflow
supports_auto_fan: true # Enable auto fan speed
supports_turbo_fan: true # Enable power cooling/heating
supports_plasma: true # Enable air purificationclimate:
- platform: lgap
id: office
name: 'Home Office'
lgap_id: lgap1
zone: 5
# Uses defaults: Low/Medium/High fan only, no swing, no plasma
# Still gets all sensors and lock controls automaticallyThe component implements rate-limiting for temperature updates (default: 5 minutes) to reduce protocol overhead. However, the first reading is always published immediately. If temperatures aren't appearing:
- Check ESPHome logs for "Processing climate message" entries
- Verify zone number matches your LG indoor unit configuration
- Adjust
temperature_publish_timeif you need more frequent updates
Lock enforcement works in two stages:
- Home Assistant requests are blocked immediately with a warning log
- Wall controller changes are detected and reverted automatically
Check ESPHome logs for messages like "Temperature change blocked - temperature lock is active" or "Mode changed at wall controller while lock active - reverting".
Fan modes are now opt-in:
- Default: Low, Medium, High only
- Auto: Requires
supports_auto_fan: true - Quiet: Requires
supports_quiet_fan: true - Turbo/Focus: Requires
supports_turbo_fan: true
This ensures a clean UI for units that don't support advanced fan modes.
Swing control is disabled by default. For ducted units with auto airflow capability, add:
supports_auto_swing: trueThis will add "Off" and "Vertical" swing options to Home Assistant.
Error codes are LG service codes. Common meanings:
0= No error (normal operation)- Non-zero values indicate service alerts or error conditions
Refer to your LG service manual for specific error code meanings, or check the ODU display panel.
Home Assistant allows current action of climate devices to be reported. This shows up in the climate card as something like "Heating to 24°C" or "Idle" etc. I haven't found in the LGAP protocol whether the compressor or heating element is actively running. This component infers the current action based on the operating mode and temperature comparison:
| Mode | Reported Action |
|---|---|
| Off | OFF |
| Cool | COOLING if current temp > target temp, otherwise IDLE |
| Heat | HEATING if current temp < target temp, otherwise IDLE |
| Heat/Cool (Auto) | COOLING if current > target, HEATING if current < target, otherwise IDLE |
| Dry | DRYING (always, when active) |
| Fan Only | FAN (always, when active) |
Note: This is an approximation. The actual compressor state may differ due to factors like defrost cycles, minimum run times, or thermostat deadbands within the unit. If you discover a byte in the LGAP protocol that indicates actual compressor status, please open an issue or PR!




