zha-device-handlers icon indicating copy to clipboard operation
zha-device-handlers copied to clipboard

Add support for Aqara Curtain Driver E1

Open EverythingSmartHome opened this issue 2 years ago • 4 comments

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.

EverythingSmartHome avatar Jul 08 '22 20:07 EverythingSmartHome

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 Coverage Status
Change from base Build 2637487176: 0.006%
Covered Lines: 5173
Relevant Lines: 7213

💛 - Coveralls

coveralls avatar Jul 08 '22 20:07 coveralls

Codecov Report

Merging #1648 (541276d) into dev (5bbe4e0) will increase coverage by 0.00%. The diff coverage is 72.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 data Powered by Codecov. Last update 5bbe4e0...541276d. Read the comment docs.

codecov-commenter avatar Jul 08 '22 20:07 codecov-commenter

Any news ? 👀

QuentinBtd avatar Aug 11 '22 08:08 QuentinBtd

Any update on when this may make it in? I have 4 of these Curtain Drivers and none show the battery or invert direction :(

kurohouou avatar Sep 22 '22 12:09 kurohouou

@dmulcahey Hello Is something missing here ?

QuentinBtd avatar Oct 18 '22 11:10 QuentinBtd

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.

circa1665 avatar Oct 24 '22 20:10 circa1665

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

EverythingSmartHome avatar Oct 24 '22 22:10 EverythingSmartHome

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.

circa1665 avatar Oct 26 '22 21:10 circa1665

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?

circa1665 avatar Nov 06 '22 22:11 circa1665

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

image

LouisAtGH avatar Feb 07 '23 09:02 LouisAtGH

This is one of the few easy to install zigbee curtain motors, and it would be wonderful to have this working correctly!

ds-sebastian avatar May 03 '23 04:05 ds-sebastian

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?

Hamish1066 avatar May 03 '23 18:05 Hamish1066

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.

cannondale0815 avatar Sep 10 '23 19:09 cannondale0815

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.

TheJulianJES avatar Oct 04 '23 03:10 TheJulianJES

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.

cannondale0815 avatar Oct 05 '23 12:10 cannondale0815

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?

TheJulianJES avatar Oct 07 '23 23:10 TheJulianJES

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)

TheJulianJES avatar Oct 07 '23 23:10 TheJulianJES

  • Superseded by https://github.com/zigpy/zha-device-handlers/pull/2629

Thanks for the PR!

TheJulianJES avatar Oct 21 '23 00:10 TheJulianJES