Native External Temperature Sensor Support for Aqara E1 TRV (ZHA)
Hi all,
Great guide @guidocioni! I’ve been down the same rabbit hole but wanted to share a different approach that uses the TRV’s native external sensor mode rather than the offset calibration workaround.
The Problem with Offset Calibration
The offset approach works, but has limitations:
- Limited to ±5°C range
- Still fundamentally using the internal sensor
- Constant offset adjustments drain battery and wear flash memory
Native External Sensor Mode
The Aqara E1 TRV actually has a built-in external sensor feature - it’s what the Aqara app uses when you pair an Aqara temperature sensor. The TRV will display and use the external temperature directly.
The catch: Nobody could get it working in ZHA because Aqara uses a proprietary binary protocol, and the TRV is a sleepy device that only stays awake for 2-3 seconds.
The Solution
After about 12 hours of reverse-engineering the Zigbee2MQTT implementation, I cracked it. The key insights:
-
Two registration messages required - You can’t just send temperature data. First you must “register” a fake external sensor with the TRV using two separate binary payloads.
-
Parallel message sending - The TRV falls asleep before sequential messages complete. The fix is using asyncio.gather() to send both registration messages simultaneously.
Custom ZHA Quirk
I’ve created a custom quirk that adds two virtual attributes to the Aqara cluster (0xFCC0):
| Attribute |
ID |
Purpose |
sensor_register |
0x0EE1 |
Write 1 for external mode, 0 for internal |
external_temperature_input |
0x0EE0 |
Write temperature in °C |
Installation
- Download the quirk file (link below)
- Place in your
custom_zha_quirks folder
- Restart Home Assistant
- The TRV will use the new quirk automatically
Enable External Sensor Mode (one-time setup)
Press the TRV button to wake it, then immediately run:
service: zha.set_zigbee_cluster_attribute
data:
ieee: "XX:XX:XX:XX:XX:XX:XX:XX" # Your TRV's IEEE
endpoint_id: 1
cluster_id: 0xfcc0
cluster_type: in
attribute: 0x0EE1
value: 1
manufacturer: 4447
The TRV display should change from showing the internal temperature to waiting for external data.
Send Temperature Updates
service: zha.set_zigbee_cluster_attribute
data:
ieee: "XX:XX:XX:XX:XX:XX:XX:XX"
endpoint_id: 1
cluster_id: 0xfcc0
cluster_type: in
attribute: 0x0EE0
value: 21.5
manufacturer: 4447
Automation Example
automation:
- alias: "Push room temperature to TRV"
trigger:
- platform: state
entity_id: sensor.room_temperature
- platform: time_pattern
minutes: "/5"
action:
- service: zha.set_zigbee_cluster_attribute
data:
ieee: "XX:XX:XX:XX:XX:XX:XX:XX"
endpoint_id: 1
cluster_id: 0xfcc0
cluster_type: in
attribute: 0x0EE0
value: "{{ states('sensor.room_temperature') | float }}"
manufacturer: 4447
Results
- TRV displays the actual external sensor temperature
- No offset limits
- No calibration automation needed
- Uses the device’s intended functionality
Technical Details
For those interested, the Aqara protocol uses:
- Attribute 0xFFF2 on cluster 0xFCC0 for binary commands
- Action code 0x02 for sensor registration
- Action code 0x05 for temperature data
- Temperature encoded as (value × 100) in big-endian float format
The full protocol documentation and quirk source code are in the GitHub link below.
Links
- Quirk file: [GitHub link to be added when PR submitted]
- PR to zha-device-handlers: [To be submitted]
This was a collaborative effort with Claude AI to decode the protocol from the Zigbee2MQTT source code. Happy to answer questions!
Edit: Confirmed working on Aqara E1 TRV (lumi.airrtc.agl001) with Home Assistant 2024.x and ZHA integration.