Here you go
============================================================================
# HAIR ESPHome Config
# ----------------------------------------------------------------------------
# Device: Athom RF IR Remote (ESP32, RF433 + IR)
# Hardware revision: v3.0.1
# Variant: full
# Contributor: DAB-LABS
# Source: https://github.com/athom-tech/esp32-configs
# License: Apache 2.0
# Tested against:
# HAIR: 0.2.0
# HA Core: 2026.5.2
# ESPHome: 2026.5.1
# Last verified: 2026-05-29
# Purpose: RX+TX
# Hardware purchase: https://www.athom.tech/
# GPIO map:
# GPIO25 IR TX (remote_transmitter)
# GPIO33 IR RX (remote_receiver, inverted)
# GPIO18 RF TX (remote_transmitter, 433 MHz)
# GPIO19 RF RX (remote_receiver, 433 MHz, inverted)
# GPIO27 Status LED
# GPIO0 Button
# Notes:
# - Standalone config -- no Athom package import. Everything is laid out
# so you can see and customize every component.
# - Based on Athom's athom-rf-ir-remote.yaml v3.0.1, set up for the
# native HA infrared platform path on HA 2026.6+.
# - RF hardware components are included but only IR is used by HAIR.
# - The legacy on_pronto bridge is preserved further down as
# commented-out historical reference for users on HA 2026.4-2026.5.
# On HA 2026.6+ it is unused.
# - BLE proxy, climate IR, and improv are included. Remove any sections
# you do not need.
# ============================================================================
# --- Substitutions ---
# Change these to match your device.
substitutions:
name: athom-rf-ir-remote
friendly_name: Athom RF IR Remote
# Athom project metadata (used by dashboard_import)
project_name: "China Athom Technology.Athom RF IR Remote"
project_version: "v3.0.1"
# Climate IR platform -- see https://esphome.io/components/climate/climate_ir/
# Valid values: coolix, daikin, fujitsu_general, gree, hitachi_ac344,
# climate_ir_lg, midea_ir, mitsubishi, tcl112, toshiba, whirlpool, etc.
#ac_platform: coolix
# --- Core ---
esphome:
name: ${name}
friendly_name: ${friendly_name}
name_add_mac_suffix: false
min_version: 2026.5.1
project:
name: "${project_name}"
version: "${project_version}"
platformio_options:
board_build.flash_mode: dio
esp32:
board: esp32dev
variant: esp32
flash_size: 8MB
framework:
type: esp-idf
version: recommended
sdkconfig_options:
CONFIG_BT_BLE_42_FEATURES_SUPPORTED: y
CONFIG_ESP_TASK_WDT_TIMEOUT_S: "10"
preferences:
flash_write_interval: 1min
# --- Network and connectivity ---
api:
encryption:
key: !secret api_encryption_key
reboot_timeout: 0s
# Start BLE scanning when API client connects, stop when it disconnects.
on_client_connected:
- esp32_ble_tracker.start_scan:
continuous: true
on_client_disconnected:
if:
condition:
not:
api.connected:
then:
- esp32_ble_tracker.stop_scan:
ota:
- platform: esphome
password: !secret ota_password
logger:
baud_rate: 0
level: DEBUG
mdns:
disabled: false
web_server:
port: 80
version: 3
wifi:
ssid: !secret wifi_ssid
password: !secret wifi_password
ap: {}
fast_connect: false
domain: ".local"
captive_portal:
# --- BLE proxy ---
# Enables Bluetooth proxy for HA. Remove this section if you do not
# need BLE forwarding and want to save memory.
esp32_ble_tracker:
scan_parameters:
continuous: false
active: true
interval: 320ms
window: 300ms
bluetooth_proxy:
active: true
esp32_improv:
authorizer: none
dashboard_import:
package_import_url: github://athom-tech/esp32-configs/athom-rf-ir-remote.yaml
# =====================================================================
# IR Hardware
# =====================================================================
# --- IR Transmitter (TX) ---
remote_transmitter:
# IR LED on GPIO25, 50% duty cycle for standard 38 kHz modulation.
- pin:
number: GPIO25
inverted: false
carrier_duty_percent: 50%
non_blocking: true
id: ir_transmitter
# RF 433 MHz transmitter on GPIO18, 100% duty (OOK modulation).
- pin:
number: GPIO18
carrier_duty_percent: 100%
non_blocking: true
id: rf_transmitter
# --- IR Receiver (RX) ---
remote_receiver:
# RF 433 MHz receiver on GPIO19.
- pin:
number: GPIO19
inverted: true
dump: all
tolerance: 25%
id: rf_receiver
# IR receiver on GPIO33. The infrared: platform below references this
# block's id to register the receiver as a native HA
# InfraredReceiverEntity, which HAIR subscribes to via
# infrared.async_subscribe_receiver() on HA 2026.6+.
#
# The legacy on_pronto event-bus bridge that HAIR used on HA
# 2026.4-2026.5 is preserved below as commented-out historical
# reference. It is no longer active and no longer needed on 2026.6+.
# Uncomment it only if you are running an older HA version that
# predates native InfraredReceiverEntity support.
- pin:
number: GPIO33
inverted: true
dump: pronto
tolerance: 25%
idle: 10ms
id: ir_receiver
# Legacy HAIR bridge (pre-2026.6). No longer needed on HA 2026.6+.
# on_pronto:
# then:
# - homeassistant.event:
# event: esphome.remote_received
# data:
# protocol: PRONTO
# code: !lambda 'return x.data;'
# =====================================================================
# HA Native Infrared Platform
# =====================================================================
# These entries register the device as infrared entities in HA. HAIR
# discovers them automatically.
#
# On HA 2026.6+:
# - TX entity (ir_proxy_transmitter) handles outbound commands.
# - RX entity (ir_proxy_receiver) is a native InfraredReceiverEntity
# and HAIR subscribes to it directly. This is the active path.
#
# On HA 2026.4-2026.5:
# - TX entity works the same way.
# - The native RX subscription is not available. To enable Sniffer
# capture on those HA versions, uncomment the on_pronto bridge in
# the remote_receiver block above.
infrared:
- platform: ir_rf_proxy
name: IR Proxy Transmitter
id: ir_proxy_transmitter
remote_transmitter_id: ir_transmitter
- platform: ir_rf_proxy
name: IR Proxy Receiver
id: ir_proxy_receiver
receiver_frequency: 38kHz
remote_receiver_id: ir_receiver
# --- RF platform (not used by HAIR) ---
radio_frequency:
- platform: ir_rf_proxy
name: 433MHz RF Transmitter
id: rf_proxy_transmitter
frequency: 433.92MHz
remote_transmitter_id: rf_transmitter
- platform: ir_rf_proxy
name: 433MHz RF Receiver
id: rf_proxy_receiver
frequency: 433.92MHz
remote_receiver_id: rf_receiver
# =====================================================================
# Climate IR
# =====================================================================
# Built-in climate entity for AC control. Uses the IR transmitter and
# receiver directly. Change the platform in substitutions to match
# your AC brand.
#climate:
# - platform: ${ac_platform}
# transmitter_id: ir_transmitter
# name: "AC"
# receiver_id: ir_receiver
# =====================================================================
# Diagnostics and UI
# =====================================================================
binary_sensor:
- platform: status
name: "Status"
entity_category: diagnostic
- platform: gpio
pin:
number: GPIO0
mode:
input: true
inverted: true
name: "Button"
disabled_by_default: true
on_multi_click:
- timing:
- ON for at least 4s
then:
- button.press: Reset
- platform: template
name: "Wall Switch"
id: wall_switch
- platform: remote_receiver
receiver_id: rf_receiver
id: rf_on_trigger
internal: false # Hides it from Home Assistant
pronto:
data : '0000006D00480000000A000C000A000C000A000C000A000C000A000C000A0035000B000B000B0035000A000C000A000C000A000C000A000D00090036000A000C000A003700090035000B000B000B000B000B000B000B000B000B0035000B000C000A000C000B000C000A000C000A000C000900370009000E0008003700090036000A000D000800370008000E0007000E0008000F0007000F0008000E0008000E000800370009000D0009000D0009000E000800380008000E0008000E0008000E000800370008000D000900370009000E000900370008000E0008000E0008000D0009000E000800380008000E000800380007000F000700380008000D0009000D0009000D0009000E0009000E0008000D0009000D0009000D00090036000A000D00090036000A0180' # Put your ON code here
on_press:
then:
- binary_sensor.template.publish:
id: wall_switch
state: ON
- platform: remote_receiver
receiver_id: rf_receiver
id: rf_off_trigger
internal: false # Hides it from Home Assistant
pronto:
data : '0000006D00480000000B000C000B000B000B000C000A0035000B000B000B000B000B000B000B0036000B000B000B000B000B000B000B000B000B0035000B000B000B0036000B0035000B000B000B000B000B000B000B000B000B0035000B000C000B000B000B000B000B000B000B000B000B0035000B000B000B0036000B0035000B000B000B0035000B000B000B000B000B000B000B000C000B000B000B000B000B0035000B000B000B000B000B000B000B0035000B000B000B000B000B000B000B0035000B000B000A0035000B000C000' # Put your ON code here
on_press:
then:
- binary_sensor.template.publish:
id: wall_switch
state: OFF
sensor:
- platform: uptime
name: "Uptime Sensor"
id: uptime_sensor
type: timestamp
entity_category: diagnostic
- platform: wifi_signal
name: "WiFi Signal dB"
id: wifi_signal_db
update_interval: 60s
entity_category: diagnostic
- platform: copy
source_id: wifi_signal_db
name: "WiFi Signal Percent"
filters:
- lambda: return min(max(2 * (x + 100.0), 0.0), 100.0);
unit_of_measurement: "Signal %"
entity_category: diagnostic
device_class: ""
button:
- platform: restart
name: "Restart"
entity_category: config
- platform: factory_reset
name: "Factory Reset"
id: Reset
entity_category: config
- platform: safe_mode
name: "Safe Mode"
internal: false
entity_category: config
- platform: template
name: "Wall Switch 1"
on_press:
- remote_transmitter.transmit_pronto:
transmitter_id: rf_transmitter
data: >-
0000 006D 0048 0000 000A 000C 000A 000C 000A 000C 000A 000C 000A 000C 000A 0035 000B 000B 000B 0035 000A 000C 000A 000C 000A 000C 000A 000D 0009 0036 000A 000C 000A 0037 0009 0035 000B 000B 000B 000B 000B 000B 000B 000B 000B 0035
000B 000C 000A 000C 000B 000C 000A 000C 000A 000C 0009 0037 0009 000E 0008 0037 0009 0036 000A 000D 0008 0037 0008 000E 0007 000E 0008 000F 0007 000F 0008 000E 0008 000E 0008 0037 0009 000D 0009 000D 0009 000E 0008 0038 0008 000E
0008 000E 0008 000E 0008 0037 0008 000D 0009 0037 0009 000E 0009 0037 0008 000E 0008 000E 0008 000D 0009 000E 0008 0038 0008 000E 0008 0038 0007 000F 0007 0038 0008 000D 0009 000D 0009 000D 0009 000E 0009 000E 0008 000D 0009 000D
0009 000D 0009 0036 000A 000D 0009 0036 000A 0180
light:
- platform: status_led
name: "Status LED"
disabled_by_default: true
pin: GPIO27
text_sensor:
- platform: wifi_info
ip_address:
name: "IP Address"
entity_category: diagnostic
ssid:
name: "Connected SSID"
entity_category: diagnostic
mac_address:
name: "Mac Address"
entity_category: diagnostic
- platform: template
name: "Last Restart"
id: device_last_restart
icon: mdi:clock
entity_category: diagnostic
time:
- platform: sntp
id: sntp_time
timezone: ""
update_interval: 6h
servers:
- 0.pool.ntp.org
- 1.pool.ntp.org
- 2.pool.ntp.org
on_time_sync:
then:
- if:
condition:
lambda: 'return id(device_last_restart).state == "";'
then:
- text_sensor.template.publish:
id: device_last_restart
state: !lambda 'return id(sntp_time).now().strftime("%a %d %b %Y - %I:%M:%S %p");'


