zha-device-handlers
zha-device-handlers copied to clipboard
Add support for Aqara Curtain Driver E1
Add's support for the Aqara curtain driver E1 - the cover itself worked OK but battery life and reversing direction did not which is what this PR adds.
Pull Request Test Coverage Report for Build 2665083943
- 27 of 37 (72.97%) changed or added relevant lines in 1 file are covered.
- No unchanged relevant lines lost coverage.
- Overall coverage increased (+0.006%) to 71.718%
| Changes Missing Coverage | Covered Lines | Changed/Added Lines | % |
|---|---|---|---|
| zhaquirks/xiaomi/aqara/curtain_e1.py | 27 | 37 | 72.97% |
| <!-- | Total: | 27 | 37 |
| Totals | |
|---|---|
| Change from base Build 2637487176: | 0.006% |
| Covered Lines: | 5173 |
| Relevant Lines: | 7213 |
💛 - Coveralls
Codecov Report
Merging #1648 (541276d) into dev (5bbe4e0) will increase coverage by
0.00%. The diff coverage is72.97%.
@@ Coverage Diff @@
## dev #1648 +/- ##
=======================================
Coverage 71.71% 71.71%
=======================================
Files 236 237 +1
Lines 7176 7213 +37
=======================================
+ Hits 5146 5173 +27
- Misses 2030 2040 +10
| Impacted Files | Coverage Δ | |
|---|---|---|
| zhaquirks/xiaomi/aqara/curtain_e1.py | 72.97% <72.97%> (ø) |
Continue to review full report at Codecov.
Legend - Click here to learn more
Δ = absolute <relative> (impact),ø = not affected,? = missing dataPowered by Codecov. Last update 5bbe4e0...541276d. Read the comment docs.
Any news ? 👀
Any update on when this may make it in? I have 4 of these Curtain Drivers and none show the battery or invert direction :(
@dmulcahey Hello Is something missing here ?
I’ve added this as a custom quirk - this currently adds support for the battery level sensor, but the reversing direction and light level sensor don’t appear to work.
I’ve added this as a custom quirk - this currently adds support for the battery level sensor, but the reversing direction and light level sensor don’t appear to work.
I never added the light level sensor, just battery and direction - both of which still work great for me personally. Perhaps someone else can give it a test to confirm either way
My bad re: light level sensor. I’m away from home for a while. But will try connecting to a spare Aqara hub and updating the e1 firmware, then repair with ZHA and see if that enables the reversing direction.
I updated the e1 firmware using an aqara hub. But no joy, reversing direction doesn't come up as an option for me.
Is there anything I can do to assist with this in terms of providing logs etc?
Would it be possible to add support for the light level sensor?
I try to get the curtain driver working, but not really success full yet:
- I am using the very latest home assistant build 2023.2.2
- with the SONOFF Zigbee 3.0 USB Dongle Plus V2 (the E-version)
- tryed with and without quirks, among them this one
Problems I have:
- the slider to control the curtain is gone (it was available when I was using older software with the Sonos P_dongle) !!
- related I can not longer use the go to position command (from Node Red and/or the gui
- battery is not working
- firmware not available
- set 0 and 100 % positions not available
- right and left setting not available (not a big problem for me, I can correct for that in Node Red)
- some other for me less important ones
Here my actual bindings

This is one of the few easy to install zigbee curtain motors, and it would be wonderful to have this working correctly!
I can't get this working. Do I need to remove at re-add the device after adding the code for the quirk to my config?
This quirk improves some aspects of the E1 curtain driver (like showing battery percentage now), but doesn't fix the issue that the curtain cannot be opened with the simple open/close controls. It only works if I use the percentage slider (0% to 100%), but not open/close. @EverythingSmartHome Would you happen to have come across this issue and have any thoughts on this?
Myself and one other person appear to be having this issue.
I'm trying to clean up this PR soon. Can you send a screenshot of how the device shows in HA (with this quirk) and what controls are working and which aren't? (up/down/stop/percentage?) Also, is it possible that like the E1 roller curtain, there's a difference depending on the firmware version?
Note for me in the future -- PR/quirk currently seems to throw an error (likely because inverted(?) attribute isn't in cache yet):
Error
Logger: homeassistant.components.zha.core.cluster_handlers
Source: zha/quirks/curtain_e1.py:48
Integration: Zigbee Home Automation (documentation, issues)
First occurred: 10:21:47 PM (2 occurrences)
Last logged: 11:29:31 PM
[0x57FD:1:0x0102]: 'async_initialize' stage failed: 23
[0x8CB3:1:0x0102]: 'async_initialize' stage failed: 23
Traceback (most recent call last):
File "/usr/src/homeassistant/homeassistant/components/zha/core/helpers.py", line 340, in wrapper
return await func(cluster_handler, *args, **kwargs)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/src/homeassistant/homeassistant/components/zha/core/cluster_handlers/__init__.py", line 389, in async_initialize
await self._get_attributes(
File "/usr/src/homeassistant/homeassistant/components/zha/core/cluster_handlers/__init__.py", line 491, in _get_attributes
read, _ = await self.cluster.read_attributes(
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.11/site-packages/zigpy/zcl/__init__.py", line 545, in read_attributes
self._update_attribute(record.attrid, value)
File "/config/zha/quirks/curtain_e1.py", line 48, in _update_attribute
if self.__getitem__(0x0017) == 1:
^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.11/site-packages/zigpy/zcl/__init__.py", line 854, in __getitem__
return self._attr_cache[self.find_attribute(key).id]
~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
KeyError: 23
Another note: for some reason, the HA PR for this was merged some time ago already. It doesn't initialize the relevant attributes.. That needs to be fixed too.
I'm trying to clean up this PR soon. Can you send a screenshot of how the device shows in HA (with this quirk) and what controls are working and which aren't? (up/down/stop/percentage?) Also, is it possible that like the E1 roller curtain, there's a difference depending on the firmware version?
Thank you! Here's additional info from my device:
Firmware: 0x0000001b Zigbee info IEEE: 54:ef:44:10:00:67:76:5d Nwk: 0xbf57 Device Type: EndDevice LQI: 87 RSSI: Unknown Last seen: 2023-10-05T08:16:53 Power source: Battery or Unknown Quirk: curtain_e1.E1Curtain
Close cover and open cover are not responding at all. Percentage slider for opening/closing is working fine, however.
I supposed it's possible that the device firmware plays a role. But I don't have any ability to update it, as I don't have the Aqara hub to perform a firmware update.
I'll happily provide any additional info you need.
Without a quirk, does the device show up as a "Router" / "Mains-Powered" or an "EndDevice"? (Wondering, because this quirk modifies the node descriptor)
Also, what about the stop action?
Quirk Test v2
"""Aqara Curtain Driver E1 device."""
from __future__ import annotations
from typing import Any
from zigpy import types as t
from zigpy.profiles import zha
from zigpy.zcl import foundation
from zigpy.zcl.clusters.closures import WindowCovering
from zigpy.zcl.clusters.general import Basic, Identify, Ota, PowerConfiguration, Time
from zigpy.zdo.types import NodeDescriptor
from zhaquirks import CustomCluster
from zhaquirks.const import (
DEVICE_TYPE,
ENDPOINTS,
INPUT_CLUSTERS,
MODELS_INFO,
NODE_DESCRIPTOR,
OUTPUT_CLUSTERS,
PROFILE_ID,
)
from zhaquirks.xiaomi import (
BasicCluster,
LUMI,
XiaomiAqaraE1Cluster,
XiaomiCluster,
XiaomiCustomDevice,
XiaomiPowerConfiguration,
)
class XiaomiAqaraDriverE1(XiaomiAqaraE1Cluster):
"""Xiaomi mfg cluster implementation specific for E1 Driver."""
attributes = XiaomiCluster.attributes.copy()
attributes.update(
{
0x0402: ("positions_stored", t.Bool, True),
0x0407: ("store_position", t.uint8_t, True),
}
)
class WindowCoveringE1(CustomCluster, WindowCovering):
"""Xiaomi Window Covering configuration cluster."""
def _update_attribute(self, attrid, value):
if attrid == WindowCovering.AttributeDefs.current_position_lift_percentage.id:
# TODO: improve this check? remove default? initialize? does the motor save this attr?
# TODO: should we do this in HA? (does it have a way now to invert motor?)
# TODO: should this always be reversed?
if (
self.get(WindowCovering.AttributeDefs.window_covering_mode.id, 0)
& WindowCovering.WindowCoveringMode.Motor_direction_reversed
):
value = 100 - value
super()._update_attribute(attrid, value)
async def command(
self,
command_id: foundation.GeneralCommand | int | t.uint8_t,
*args: Any,
manufacturer: int | t.uint16_t | None = None,
expect_reply: bool = True,
tsn: int | t.uint8_t | None = None,
**kwargs: Any,
) -> Any:
"""Overwrite the commands to make it work for both firmware 1425 and 1427.
We either overwrite analog_output's current_value or multistate_output's current
value to make the roller work.
"""
if command_id == WindowCovering.ServerCommandDefs.up_open.id:
command_id = WindowCovering.ServerCommandDefs.go_to_lift_percentage.id
args = (0,) # TODO: inverted?
elif command_id == WindowCovering.ServerCommandDefs.down_close.id:
command_id = WindowCovering.ServerCommandDefs.go_to_lift_percentage.id
args = (100,) # TODO: inverted?
# elif command_id == WindowCovering.ServerCommandDefs.go_to_lift_percentage.id:
# args = (100 - args[0],) # TODO: needed to invert here? (depending on attr?)
return await super().command(
command_id,
*args,
manufacturer=manufacturer,
expect_reply=expect_reply,
tsn=tsn,
**kwargs,
)
# TODO: check if cluster works
# TODO: currently duplicated with roller_curtain_e1.py
class PowerConfigurationDriverE1(XiaomiPowerConfiguration):
"""Power cluster which ignores Xiaomi voltage reports."""
def _update_battery_percentage(self, voltage_mv: int) -> None:
"""Ignore Xiaomi voltage reports, so they're not used to calculate battery percentage."""
# This device sends battery percentage reports which are handled using a XiaomiCluster and
# the inherited XiaomiPowerConfiguration cluster.
# This device might also send Xiaomi battery reports, so we only want to use those for the voltage attribute,
# but not for the battery percentage. XiaomiPowerConfiguration.battery_reported() still updates the voltage.
class DriverE1(XiaomiCustomDevice):
"""Aqara Curtain Driver E1 device."""
signature = {
MODELS_INFO: [(LUMI, "lumi.curtain.agl001")],
ENDPOINTS: {
# <SizePrefixedSimpleDescriptor endpoint=1 profile=260 device_type=263
# device_version=1
# input_clusters=[0, 1, 3, 10, 258, 64704]
# output_clusters=[3, 10, 25, 64704]>
1: {
PROFILE_ID: zha.PROFILE_ID,
DEVICE_TYPE: zha.DeviceType.OCCUPANCY_SENSOR,
INPUT_CLUSTERS: [
Basic.cluster_id,
PowerConfiguration.cluster_id,
Identify.cluster_id,
Time.cluster_id,
WindowCovering.cluster_id,
XiaomiAqaraDriverE1.cluster_id,
],
OUTPUT_CLUSTERS: [
Identify.cluster_id,
Time.cluster_id,
Ota.cluster_id,
XiaomiAqaraDriverE1.cluster_id,
],
}
},
}
replacement = {
# TODO: needed?
# NODE_DESCRIPTOR: NodeDescriptor(
# 0x02, 0x40, 0x80, 0x115F, 0x7F, 0x0064, 0x2C00, 0x0064, 0x00
# ),
ENDPOINTS: {
1: {
PROFILE_ID: zha.PROFILE_ID,
DEVICE_TYPE: zha.DeviceType.WINDOW_COVERING_DEVICE,
INPUT_CLUSTERS: [
BasicCluster,
PowerConfigurationDriverE1,
Identify.cluster_id,
Time.cluster_id,
WindowCoveringE1,
XiaomiAqaraDriverE1,
],
OUTPUT_CLUSTERS: [
Identify.cluster_id,
Time.cluster_id,
Ota.cluster_id,
# XiaomiAqaraDriverE1, # TODO: keep in OUTPUT too?
],
}
},
}
Please test the quirk above and let me know what works and what doesn't (and what "powered by" status is shown).
-> Let's move that discussion to https://github.com/zigpy/zha-device-handlers/pull/2629
EDIT: also use the quirk from the link in the PR (not the one above)
- Superseded by https://github.com/zigpy/zha-device-handlers/pull/2629
Thanks for the PR!