Automating heating with smart thermostatic radiator valves, the full guide


picture probably helps more than code , these are partial screenshots but there are just big lists of similar conditions

fairly simple just a bit verbose adding them all in if you have a big house

as noted by having this verbose you can disable individual lines temporarily

hot water control was much more fun but doesn’t belong here

also see
nest integration

oh and check out scheduler card, much simpler for schedules

note also how the entities here are the underlying valves not the Better Thermostat entities (some of which are one entity for up to 3 valves). Better Thermostat has been rock solid for me.

some of my TRVs run on rechargeable batteries without issues… but they are rapthor lithium ones… they seem very good.

Hi,
Thanks for the complete guide but unfortunately, I can not perform the calibration step as mentioned here TRV Calibrator - Calibrate your valve with an external sensor (probably TRV agnostic).
When I configure the blueprint, I don’t have any TRV Calibration Entity.
This is strange because I have the same TRV as yours in the post.
I also notice that in the control dashboard, mine doesn’t have a proper name, only “switch”.
Do you have any idea how to fix it?

I had to rename the entities the first time i installed it, so that’s no potential source of differences. Honestly I’m not sure what could be the issue, maybe you’re missing some hidden entities?
You can still try to re-initialize the trv, sometimes the first installation through zha doesn’t work properly

I appreciate this is an old post, but maybe someone can still help. I really don’t know what I’m doing. I have created a schedule. How/where do I create an automation?
You say in the helpers section, but I can’t see anything.

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:

  1. 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.

  2. 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

  1. Download the quirk file (link below)
  2. Place in your custom_zha_quirks folder
  3. Restart Home Assistant
  4. 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.

1 Like

Hi @voipguy , looks like you did some amazing work, when would you be able to share those files? Thank you!

Hi. Thanks for the guide. You said you found Better Thermostat unreliable. Does that go for versatile_thermostat repo also? I heard good things about it and it seems highly customizable.

never tried it