Skip to content

Conversation

@cl445
Copy link

@cl445 cl445 commented Dec 29, 2025

Summary

Improves the Aqara E1 Radiator Thermostat (AGL001) quirk with heartbeat parsing, firmware version display, tested attribute types, and migrates to QuirkBuilder v2 API.

Features

  • Heartbeat parsing (attribute 0x00F7) to extract device/local temperature, battery, preset, valve alarm, firmware version
  • DeviceTemperature cluster for internal TRV temperature sensor
  • Read/write support with Xiaomi manufacturer code for all manufacturer-specific attributes
  • Calibrate button (attribute 0x0270) to start valve calibration - now exposed as button entity!
  • Setup mode detection (preset=3)
  • Firmware version displayed from heartbeat data (updates Basic.sw_build_id)
  • Running state simulation - HVAC action derived from temperature difference (fixes "unknown" state)

QuirkBuilder v2 Migration

Migrated from v1 (CustomDevice with signature/replacement) to v2 (QuirkBuilder) API with complete entity definitions:

  • Switches: child_lock, window_detection, valve_detection, schedule
  • Binary sensors: valve_alarm, window_open, calibrated
  • Selects: preset, sensor_mode
  • Number: away_preset_temperature (5-30°C, 0.5 step)
  • Button: calibrate (triggers valve calibration)

⚠️ Known Issue: Duplicate Entities
ZHA currently has legacy hardcoded entity definitions for this device in zha/application/platforms/. This causes duplicate entities (e.g., "Child lock" and "Kindersicherung"). These legacy definitions should be removed from ZHA in a follow-up PR once this quirk is merged.

Running State Simulation

The Aqara E1 TRV doesn't report running_state, so we simulate it based on the heartbeat data:

  • Heating when setpoint > local_temperature and system is on
  • Idle when at/above setpoint or system is off

This fixes the "unknown" HVAC action in Home Assistant.

Attribute Types (tested on real device)

All manufacturer-specific attributes now use correct ZCL types that the device accepts:

Attribute Type Notes
child_lock t.uint8_t Device expects type 0x20, not Bool
preset t.uint8_t Device expects type 0x20, not enum8
system_mode t.uint8_t Device expects type 0x20, not enum8
sensor t.uint8_t Device expects type 0x20, not enum8
away_preset_temperature t.uint32_t ZHA sends centidegrees, no conversion needed
window_detection, valve_detection, etc. t.uint8_t Boolean attributes as uint8

Code Quality

  • XIAOMI_MANUFACTURER_CODE constant instead of magic number
  • Refactored heartbeat parser with _ZCL_TYPE_SIZES lookup table
  • Comprehensive type hints

Related Work

This PR focuses on:

  • ✅ Heartbeat parsing for telemetry data
  • ✅ Correct attribute types for write support
  • ✅ DeviceTemperature cluster (internal TRV sensor)
  • ✅ Manufacturer code handling for read/write
  • ✅ Firmware version from heartbeat
  • QuirkBuilder v2 with entity definitions
  • Calibrate button entity
  • Running state simulation (HVAC action)

Out of scope (follow-up PR):

The following features require more complex implementation and should be addressed in a separate PR:

  • External sensor switching (internal/external)
  • External temperature input

External temperature sensor functionality is being addressed in separate PRs:

Changes

  • Migrate to QuirkBuilder v2 API with complete entity definitions
  • Add _parse_heartbeat() function for TLV-encoded heartbeat messages
  • Add LocalDeviceTemperatureCluster for device temperature
  • Update local_temperature on thermostat from heartbeat
  • Fix attribute types based on real device testing
  • Add firmware version parsing to Basic cluster
  • Add running_state simulation based on temperature difference
  • Add tests for heartbeat parsing, write_attributes, and running_state
  • Update tests to use zigpy_device_from_v2_quirk fixture

Test plan

  • All existing tests pass (43 thermostat tests)
  • New heartbeat parsing tests added
  • Running state simulation tests added
  • Tested on real Aqara E1 TRV device:
    • Child Lock toggle ✅
    • Preset changes (Manual/Auto/Away) ✅
    • Away Preset Temperature ✅
    • Calibrate button ✅
    • Firmware version from heartbeat ✅

@codecov
Copy link

codecov bot commented Dec 29, 2025

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 92.35%. Comparing base (ec82cb8) to head (678098e).

Additional details and impacted files
@@            Coverage Diff             @@
##              dev    #4604      +/-   ##
==========================================
+ Coverage   92.28%   92.35%   +0.07%     
==========================================
  Files         371      371              
  Lines       12155    12271     +116     
==========================================
+ Hits        11217    11333     +116     
  Misses        938      938              

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@cl445 cl445 force-pushed the improve-aqara-e1-trv branch 2 times, most recently from 61a26bc to dce0a7a Compare December 29, 2025 00:35
Features:
- Add heartbeat parsing (attribute 0x00F7) to extract:
  - Device temperature → new LocalDeviceTemperatureCluster
  - Local temperature → Thermostat.local_temperature
  - Battery percentage, preset, valve alarm, power outage count

- Add DeviceTemperature cluster for internal TRV temperature

- Add read/write support with Xiaomi manufacturer code for all
  manufacturer-specific attributes

- Add calibrate attribute (0x0270) to trigger valve calibration

- Add setup mode detection (preset=3, "E11" on display)

Type-safe enums (improvement over Z2M):
- SystemMode: Off, Heat
- Preset: Manual, Auto, Away, Setup
- SensorMode: Internal, ExternalPaired, ExternalInput
  (Z2M maps both external modes to just "external")

Code quality improvements:
- Use t.Bool for boolean attributes (window_detection, valve_detection,
  valve_alarm, child_lock, window_open, calibrated, schedule)
- Define XIAOMI_MANUFACTURER_CODE constant
- Refactor heartbeat parser with _ZCL_TYPE_SIZES lookup table
- Add comprehensive type hints

Tests:
- Add tests for heartbeat parsing

Based on zigbee2mqtt/zigbee-herdsman-converters implementation.

WIP: External sensor switching and external temperature input
     not yet implemented.
@cl445 cl445 force-pushed the improve-aqara-e1-trv branch from dce0a7a to a43eb46 Compare December 29, 2025 00:39
Test coverage for:
- Float parsing (type 0x39)
- Signed integers (int8, int16, int32)
- Unknown data type handling
- Valve alarm updates from heartbeat
- Calibrate attribute write
- Away preset temperature conversion
- Attribute write by name
- Preset setup mode detection
Coverage for:
- Truncated heartbeat data (IndexError/struct.error handling)
- Unknown attribute name in write_attributes
@GamerClassN7
Copy link

GamerClassN7 commented Dec 29, 2025

@cl445 I worked on similar quirk in past how, to make calibratiuon working. Without luck, it is nice to se that someone was able to do it how ever, most of time i try to set temperaturute /mod/something i get

Failed to perform the action switch/turn_on. Failed to write attribute child_lock=1:
<Status.INVALID_DATA_TYPE: 141>

Failed to perform the action number/set_value. Failed to write attribute away_preset_temperature=1200: <Status.INVALID_VALUE: 135>

notification in HA any idea why ?

@cl445
Copy link
Author

cl445 commented Dec 29, 2025

@GamerClassN7 I opened this PR as WIP on purpose, to get early feedback and maybe some help. Thanks!
The initial implementation simply mirrors zigbee2mqtt’s behavior. If it works there, it should work here too. I’ll test it in my next time slot.

@GamerClassN7
Copy link

@GamerClassN7 I opened this PR as WIP on purpose, to get early feedback and maybe some help. Thanks! The initial implementation simply mirrors zigbee2mqtt’s behavior. If it works there, it should work here too. I’ll test it in my next time slot.

I hame small experience with adding stuff to quirk if you use quirks V2 you can even add calibration button:
#3371

it is then autmatically added to card in HASS. if you need any testing i am happy to help :)

Tested on real device via Home Assistant:
- child_lock, preset, system_mode: Use t.uint8_t instead of t.Bool/enums
  (device expects ZCL type 0x20, not Bool or enum8)
- away_preset_temperature: Use t.uint32_t, no *100 multiplication
  (ZHA Number entity already sends centidegrees)
- sensor attribute: Use t.uint8_t instead of SensorMode enum
- Add firmware version parsing from heartbeat to Basic cluster

All write operations now work correctly on real hardware.
@cl445
Copy link
Author

cl445 commented Dec 31, 2025

Real Device Testing Complete

I've tested all write operations on an actual Aqara E1 TRV and fixed the attribute types based on the device's responses:

Fixed Issues from @GamerClassN7's Report:

  1. child_lock INVALID_DATA_TYPE - Fixed by using t.uint8_t instead of t.Bool. The device expects ZCL type 0x20, not Bool.

  2. away_preset_temperature INVALID_VALUE - Fixed by:

    • Using t.uint32_t (device expects type 0x23)
    • Removing the *100 multiplication (ZHA Number entity already sends centidegrees)
  3. preset INVALID_DATA_TYPE - Fixed by using t.uint8_t instead of the Preset enum.

Additional Changes:

  • All enum attributes (system_mode, sensor) now use t.uint8_t
  • Added firmware version parsing from heartbeat data
  • Updated tests to reflect the new behavior

All operations now work correctly on real hardware!

@cl445 cl445 changed the title [WIP] Improve Aqara E1 TRV with heartbeat parsing and write support Improve Aqara E1 TRV (AGL001) with heartbeat parsing and write support Dec 31, 2025
@cl445 cl445 marked this pull request as ready for review December 31, 2025 00:26
@Elyasin
Copy link
Contributor

Elyasin commented Jan 1, 2026

Thank you. I am looking forward to see this reviewed and approved.

Looks good to me. I hope I'll get to try it as custom quirk on my home assistant and might give feedback.

@GamerClassN7
Copy link

GamerClassN7 commented Jan 1, 2026

@cl445 I can confirm that the type errors are fixed 👍
I would suggest adding the calibrate button entity to Home Assistant, as many users are not aware that it’s even possible to send custom ZHA commands.

Also, I think the HCAC Action entity should be removed, since it is not used by this model.
image

Do you have any idea how to get external sensor calibration working?
I was able to get it working in my version of the quirk, but not in yours, even though I can see the required cluster attributes are present in your implementation. Onriginaly implementation was taken from another MR: https://github.com/zigpy/zha-device-handlers/pull/2802/changes i think this is last functionalitiy exept schedule string, whitch is supported by Z
M and not ZHA (It vould be nice to have it finally fully supported as you suggested)

Reference:

also want to ask is there reason why are you using original v1 iteragion of quirks and not V2 whitch is mutch more easeally implementable on side of HASS ? :)

Thank you once morre for your hard work !!!!

cl445 and others added 2 commits January 2, 2026 01:19
- Replace AGL001 class with QuirkBuilder pattern
- Add complete entity definitions:
  - Switches: child_lock, window_detection, valve_detection, schedule
  - Binary sensors: valve_alarm, window_open, calibrated
  - Selects: preset, sensor_mode
  - Number: away_preset_temperature
  - Button: calibrate (triggers valve calibration)
- Update tests to use zigpy_device_from_v2_quirk fixture

Note: ZHA has legacy hardcoded entities for this device which may
cause duplicates. Those should be removed from ZHA in a follow-up.
@cl445
Copy link
Author

cl445 commented Jan 2, 2026

@GamerClassN7 Thanks for your feedback! I've now addressed your suggestions:

  1. Migrated to QuirkBuilder v2 API - The quirk now uses the v2 API with complete entity definitions.

  2. Calibrate button added - Using .write_attr_button() to expose calibration as a button entity in Home Assistant.

  3. HVAC Action entity - This is created automatically by ZHA for all Thermostat clusters, not by the quirk. Removing it would require changes in ZHA core, not here.

  4. Duplicate entities - I noticed that ZHA has legacy hardcoded entity definitions for this device in zha/application/platforms/. This causes duplicates. I've documented this in the PR description - these should be removed from ZHA in a follow-up once this quirk is merged.

  5. External sensor - Still out of scope for this PR, as mentioned. The protocol is complex and being addressed in other PRs (Supports external temp sensor for Aqara E1 TRV (continues PR#3974) #4463, Add support for Aqara external temperature sensor #3974).

Please test the updated version when you have time!

The Aqara E1 TRV doesn't report running_state, but we can derive it
from the heartbeat data:
- Heat_State_On when setpoint > local_temperature and system is on
- Idle (0) when at/above setpoint or system is off

This fixes the "unknown" HVAC action in Home Assistant.
@cl445
Copy link
Author

cl445 commented Jan 2, 2026

@GamerClassN7 I've also added running_state simulation now!

The Aqara E1 TRV doesn't report running_state natively, so we now derive it from the heartbeat data:

  • Heating when setpoint > local_temperature and system is on
  • Idle when at/above setpoint or system is off

This should fix the "unknown" HVAC action in Home Assistant. 🎉

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants