Aqara E1 External Temperature Calibration
This blueprint synchronizes an external temperature sensor with the Aqara Smart Radiator Thermostat E1 via Zigbee2MQTT.
Why use this?
Unlike software-based solutions (like Better Thermostat), this blueprint feeds the temperature directly into the Aqara hardware. This allows the TRV to use its own internal Proportional-Integral (PI) logic while benefiting from a more accurate sensor placement away from the radiator.
Prerequisites
- Zigbee2MQTT: You must set the
sensorsetting toexternalin the “Exposes” tab of your device in Z2M. - MQTT: Home Assistant must be connected to the same broker as Z2M.
Features
- Integer Rounding: Automatically handles the Aqara E1 requirement for whole numbers.
- Keep-Alive Heartbeat: Sends a periodic update to prevent the TRV from reverting to its internal sensor.
- Change Limiting: Prevents sudden spikes that might trigger false window-open detection.
- Robust Logic: Handles “unavailable” or “unknown” states gracefully without crashing.
Installation
Click the “Import Blueprint” button above and paste your Gist URL, or manually add the YAML to your blueprints/automation folder.
Crucial Zigbee2MQTT users: force_update
By default, Zigbee2MQTT only tells Home Assistant when a temperature changes. If your room stays at exactly 21°C for two hours, Z2M sends nothing. After 120 minutes of silence, the Aqara E1 will think the connection is lost and revert to its internal sensor.
To fix this, you must tell Z2M to report the temperature even if it hasn’t changed:
- Open your Zigbee2MQTT configuration folder (usually via the File Editor or Samba).
- Open
devices.yaml. - Find your External Temperature Sensor (not the TRV) and add
force_update: trueto its entry.
Example:
'0x00158d0001234567':
friendly_name: Living Room T-RH
force_update: true
The Code
Click to view the Blueprint YAML
blueprint:
domain: automation
name: Aqara E1 External Temperature Sensor Calibration
author: Proxo Nox
description: |
# Aqara Smart Radiator Thermostat E1 External Temperature Sensor Calibration
**Inspired by and based on** the "Sonoff TRVZB External Temperature Sensor Calibration" blueprint by photomoose (Tom Davis).
## Important Distinction: What This Blueprint Does vs. Better Thermostat
This blueprint is **not** a replacement for Better Thermostat. They serve different purposes:
| **This Blueprint** | **Better Thermostat** |
|-------------------|----------------------|
| Feeds external temperature **directly to the thermostat hardware** | Creates a virtual thermostat in software |
| Thermostat uses its own algorithms and schedules | HA handles all control logic |
| Works even if Home Assistant is down (once configured) | Requires HA to be running constantly |
| Native integration with the device's features | More flexible control options |
**Use this blueprint when:** You want the Aqara E1 to use its own scheduling and algorithms but with a more accurately placed temperature sensor.
**Use Better Thermostat when:** You want Home Assistant to handle all temperature control logic across multiple rooms/zones.
## Description
This blueprint synchronises the temperature reading of an Aqara Smart Radiator Thermostat E1 with an external temperature sensor. When configured in "external sensor" mode in Zigbee2MQTT, the Aqara E1 completely bypasses its internal temperature sensor and relies entirely on values sent to its `external_temperature_input`.
## How It Works
Whenever your external temperature sensor reports a new value, this automation:
1. Reads the temperature from your external sensor
2. Validates it's a proper number (ignores unavailable/unknown states)
3. Rounds it to the nearest whole number (the Aqara E1 accepts only integer values)
4. Optionally limits how much the temperature can change at once (prevents false window detection)
5. Sends the value directly to the thermostat via MQTT
Additionally, a keep-alive timer ensures the thermostat never reverts to its internal sensor during periods of constant temperature.
## Important for Zigbee2MQTT Users:
To prevent the thermostat from reverting to the internal sensor during stable temperatures,
enable `force_update: true` for your external temperature sensor in the Zigbee2MQTT
`devices.yaml` file. This ensures the "Keep-alive" heartbeat always has a value to send.
## Requirements
- **Hardware**: Aqara Smart Radiator Thermostat E1 (firmware latest)
- **Integration**: Zigbee2MQTT (latest version) with MQTT configured in Home Assistant
- **Configuration**: Thermostat must be set to `sensor: external` in Zigbee2MQTT device settings
- **External Sensor**: Any temperature sensor that reports to Home Assistant (Zigbee, WiFi, etc.)
## Features
- **Integer rounding**: Automatically rounds temperatures to nearest whole number (required by device)
- **Change limiting**: Prevents sudden temperature changes that could trigger false window detection
- **Timeout prevention**: Regular updates ensure thermostat never reverts to internal sensor
- **Flexible naming**: You provide the exact Zigbee2MQTT device name for reliable MQTT communication
- **Validation**: Ensures values stay within valid range (0-55°C)
- **Defensive programming**: Handles network hiccups, unavailable entities, and missing attributes gracefully
- **Debug logging**: Optional logging to help troubleshoot
## Configuration in Zigbee2MQTT
Before using this blueprint, you must configure your Aqara E1 in Zigbee2MQTT:
1. Go to Zigbee2MQTT → Devices → Your Aqara E1
2. Find the `sensor` setting (under "Exposes")
3. Change it from `internal` to `external`
4. The device will now listen to the `external_temperature_input` for temperature values
## Blueprint Inputs
| Input | Description |
|-------|-------------|
| External Temperature Sensor | The sensor entity that provides the room temperature |
| Aqara E1 Climate Entity | The climate entity of your Aqara E1 thermostat |
| Zigbee2MQTT Device Name | The exact device name as shown in Zigbee2MQTT (e.g., "living_room_valve" or "0x54ef4410009f546f") |
| Maximum Temperature Change | Optional limit to prevent window detection (set 0 to disable) |
| Update Frequency | How often to send updates during constant temperatures |
| Enable Debug Logging | Turn on/off system log entries |
## Version History
- v1.7 - Final stable release with robust datetime and attribute handling.
---
*Based on the original Sonoff TRVZB blueprint by photomoose (Tom Davis). Adapted for Aqara E1 with community contributions.*
input:
external_temperature_sensor:
name: External Temperature Sensor
description: "The sensor entity that provides the accurate room temperature"
selector:
entity:
filter:
- domain: sensor
device_class: temperature
multiple: false
aqara_climate_entity:
name: Aqara E1 Climate Entity
description: "The climate entity of your Aqara E1 thermostat"
selector:
entity:
filter:
- domain: climate
multiple: false
zigbee2mqtt_device_name:
name: Zigbee2MQTT Device Name
description: "The exact friendly name or IEEE address in Zigbee2MQTT."
selector:
text:
max_temperature_change:
name: Maximum Temperature Change (per update)
description: "Limits rapid changes. Set to 0 to disable."
default: 3
selector:
number:
min: 0
max: 10
step: 1
unit_of_measurement: "°C"
mode: slider
update_frequency:
name: Update Frequency (minutes)
description: "Keep-alive interval to prevent reversion to internal sensor."
default: 30
selector:
number:
min: 5
max: 120
step: 5
unit_of_measurement: "minutes"
mode: slider
enable_debug:
name: Enable Debug Logging
default: false
selector:
boolean:
trigger_variables:
external_sensor: !input external_temperature_sensor
climate_entity: !input aqara_climate_entity
device_name: !input zigbee2mqtt_device_name
max_change: !input max_temperature_change
update_mins: !input update_frequency
debug: !input enable_debug
triggers:
- trigger: state
entity_id: !input external_temperature_sensor
- trigger: time_pattern
minutes: "/5"
variables:
climate_state_raw: "{{ states[climate_entity] }}"
current_thermostat_temp: >-
{% set temp = state_attr(climate_entity, 'current_temperature') %}
{{ temp | float(20) if temp is not none else 20 }}
last_changed_datetime: >-
{% if climate_state_raw is not none and climate_state_raw is not string %}
{{ climate_state_raw.last_changed }}
{% else %}
{{ now() }}
{% endif %}
minutes_since_last: >-
{% set last = last_changed_datetime | as_datetime(now()) %}
{{ ((now() - last).total_seconds() / 60) | int }}
should_run: >-
{% if trigger.platform == 'state' %}
true
{% elif trigger.platform == 'time_pattern' %}
{{ minutes_since_last >= update_mins }}
{% else %}
false
{% endif %}
raw_state: "{{ states(external_sensor) }}"
valid_temperature: >-
{% if raw_state not in ['unavailable', 'unknown', 'none'] and raw_state is not none and raw_state != '' %}
{% if raw_state | float(none) is not none %}
true
{% else %}
false
{% endif %}
{% else %}
false
{% endif %}
base_temperature: >-
{% if valid_temperature %}
{{ raw_state | float(0) }}
{% else %}
{{ current_thermostat_temp }}
{% endif %}
rounded_temperature: "{{ base_temperature | round(0) | int }}"
limited_temperature: >-
{% if max_change | int > 0 and current_thermostat_temp > 0 %}
{% set diff = (rounded_temperature - current_thermostat_temp) | abs %}
{% if diff > max_change %}
{% if rounded_temperature > current_thermostat_temp %}
{{ (current_thermostat_temp + max_change) | int }}
{% else %}
{{ (current_thermostat_temp - max_change) | int }}
{% endif %}
{% else %}
{{ rounded_temperature | int }}
{% endif %}
{% else %}
{{ rounded_temperature | int }}
{% endif %}
safe_temperature: >-
{% if limited_temperature < 0 %}
{{ 0 }}
{% elif limited_temperature > 55 %}
{{ 55 }}
{% else %}
{{ limited_temperature }}
{% endif %}
temperature_changed: "{{ safe_temperature != current_thermostat_temp }}"
actions:
- condition: template
value_template: "{{ should_run }}"
- condition: template
value_template: "{{ valid_temperature or trigger.platform == 'time_pattern' }}"
- choose:
- conditions:
- "{{ temperature_changed or debug or trigger.platform == 'time_pattern' }}"
sequence:
- action: mqtt.publish
data:
topic: "zigbee2mqtt/{{ device_name }}/set"
payload: "{\"external_temperature_input\": {{ safe_temperature }}}"
qos: 1
retain: false
- if:
- "{{ debug }}"
then:
- action: system_log.write
data:
level: info
message: >-
Aqara E1 Update - Sent: {{ safe_temperature }}°C | Prev: {{ current_thermostat_temp }}°C | Trigger: {{ trigger.platform }}
mode: single
max_exceeded: silent