Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ZHA - bellows.ezsp error AttributeError: No such command name: battery_percent_reported #101996

Closed
SGXander opened this issue Oct 14, 2023 · 10 comments
Assignees

Comments

@SGXander
Copy link

The problem

Logs below are reported very frequently. Not sure what is causing it but no automations call that command as far as I know.

What version of Home Assistant Core has the issue?

20232.10.2

What was the last working version of Home Assistant Core?

2023.9.3

What type of installation are you running?

Home Assistant OS

Integration causing the issue

ZHA

Link to integration documentation on our website

https://www.home-assistant.io/integrations/zha/

Diagnostics information

No response

Example YAML snippet

No response

Anything in the logs that might be useful for us?

Logger: bellows.ezsp
Source: /usr/local/lib/python3.11/site-packages/bellows/ezsp/__init__.py:491
First occurred: 01:49:52 (204 occurrences)
Last logged: 13:06:25
Exception running handler

Traceback (most recent call last):
  File "/usr/local/lib/python3.11/site-packages/bellows/ezsp/__init__.py", line 489, in handle_callback
    handler(*args)
  File "/usr/local/lib/python3.11/site-packages/bellows/zigbee/application.py", line 508, in ezsp_callback_handler
    self._handle_frame(*args)
  File "/usr/local/lib/python3.11/site-packages/bellows/zigbee/application.py", line 553, in _handle_frame
    self.packet_received(
  File "/usr/local/lib/python3.11/site-packages/zigpy/application.py", line 1010, in packet_received
    self.handle_message(
  File "/usr/local/lib/python3.11/site-packages/zigpy/application.py", line 526, in handle_message
    return sender.handle_message(
           ^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/zigpy/device.py", line 367, in handle_message
    self.endpoints[src_ep].handle_message(
  File "/usr/local/lib/python3.11/site-packages/zigpy/endpoint.py", line 235, in handle_message
    handler(hdr, args, dst_addressing=dst_addressing)
  File "/usr/local/lib/python3.11/site-packages/zigpy/zcl/__init__.py", line 430, in handle_message
    self.handle_cluster_general_request(hdr, args, dst_addressing=dst_addressing)
  File "/usr/local/lib/python3.11/site-packages/zigpy/zcl/__init__.py", line 476, in handle_cluster_general_request
    self._update_attribute(attr.attrid, value)
  File "/usr/local/lib/python3.11/site-packages/zhaquirks/xiaomi/__init__.py", line 325, in _update_attribute
    self.endpoint.power.battery_percent_reported(
    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/zigpy/zcl/__init__.py", line 845, in __getattr__
    raise AttributeError(f"No such command name: {name}")
AttributeError: No such command name: battery_percent_reported

Additional information

No response

@home-assistant
Copy link

Hey there @dmulcahey, @Adminiuga, @puddly, mind taking a look at this issue as it has been labeled with an integration (zha) you are listed as a code owner for? Thanks!

Code owner commands

Code owners of zha can trigger bot actions by commenting:

  • @home-assistant close Closes the issue.
  • @home-assistant rename Awesome new title Renames the issue.
  • @home-assistant reopen Reopen the issue.
  • @home-assistant unassign zha Removes the current integration label and assignees on the issue, add the integration domain after the command.

(message by CodeOwnersMention)


zha documentation
zha source
(message by IssueLinks)

@puddly
Copy link
Contributor

puddly commented Oct 14, 2023

What Aqara devices do you have on your network?

@SGXander
Copy link
Author

What Aqara devices do you have on your network?

All of them :D
7 opening sensors, 2 roller blind controllers, one button and one temp/humidity sensor.

@TheJulianJES
Copy link
Member

There's some faulty quirk then. Are you using any custom quirks for the roller blind controllers?
If so, can you link them?

Otherwise, please check if the logs contain more information (above the exception). If not, enable debug logs through the integration page, wait a bit until the issue reproduces, then disable debug logs through the integrations page. That will prompt you to download them.
Post the relevant lines here (above the exceptions) or, if you want, you can send me the complete log at [email protected].

@SGXander
Copy link
Author

SGXander commented Oct 15, 2023

Interesting. The only quirk file I have is for the roller blinds as they simply refused to operate correctly without it. I'll try and find the forum post I got it from. The .py is below but I can't find any "battery_percent_reported" in it...

"""Aqara Roller Shade 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 (
    Alarms,
    AnalogOutput,
    Basic,
    DeviceTemperature,
    GreenPowerProxy,
    Groups,
    Identify,
    MultistateOutput,
    OnOff,
    Ota,
    PowerConfiguration,
    Scenes,
    Time,
)
from zigpy.zcl.clusters.manufacturer_specific import ManufacturerSpecificCluster

from zhaquirks import Bus, CustomCluster, LocalDataCluster
from zhaquirks.const import (
    DEVICE_TYPE,
    ENDPOINTS,
    INPUT_CLUSTERS,
    MODELS_INFO,
    OUTPUT_CLUSTERS,
    PROFILE_ID,
)
from zhaquirks.xiaomi import LUMI, BasicCluster, XiaomiCluster, XiaomiCustomDevice

PRESENT_VALUE = 0x0055
CURRENT_POSITION_LIFT_PERCENTAGE = 0x0008
GO_TO_LIFT_PERCENTAGE = 0x0005
DOWN_CLOSE = 0x0001
UP_OPEN = 0x0000
STOP = 0x0002


class XiaomiAqaraRollerE1(XiaomiCluster, ManufacturerSpecificCluster):
    """Xiaomi mfg cluster implementation specific for E1 Roller."""

    cluster_id = 0xFCC0

    attributes = XiaomiCluster.attributes.copy()
    attributes.update(
        {
            0x0400: ("reverse_direction", t.Bool, True),
            0x0402: ("positions_stored", t.Bool, True),
            0x0407: ("store_position", t.uint8_t, True),
            0x0408: ("speed", t.uint8_t, True),
            0x0409: ("charging", t.uint8_t, True),
            0x00F7: ("aqara_attributes", t.LVBytes, True),
        }
    )


class AnalogOutputRollerE1(CustomCluster, AnalogOutput):
    """Analog output cluster, only used to relay current_value to WindowCovering."""

    cluster_id = AnalogOutput.cluster_id

    def __init__(self, *args: Any, **kwargs: Any) -> None:
        """Init."""
        super().__init__(*args, **kwargs)

        self._update_attribute(0x0041, float(0x064))  # max_present_value
        self._update_attribute(0x0045, 0.0)  # min_present_value
        self._update_attribute(0x0051, 0)  # out_of_service
        self._update_attribute(0x006A, 1.0)  # resolution
        self._update_attribute(0x006F, 0x00)  # status_flags

    def _update_attribute(self, attrid: int, value: Any) -> None:

        super()._update_attribute(attrid, value)

        if attrid == PRESENT_VALUE:
            self.endpoint.window_covering._update_attribute(
                CURRENT_POSITION_LIFT_PERCENTAGE, (100 - value)
            )


class WindowCoveringRollerE1(CustomCluster, WindowCovering):
    """Window covering cluster to receive commands that are sent to the AnalogOutput's present_value to move the motor."""

    cluster_id = WindowCovering.cluster_id

    def __init__(self, *args: Any, **kwargs: Any) -> None:
        """Init."""
        super().__init__(*args, **kwargs)

    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,
        tries: int = 1,
        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 == UP_OPEN:
            (res,) = await self.endpoint.multistate_output.write_attributes(
                {"present_value": 1}
            )
            return foundation.GENERAL_COMMANDS[
                foundation.GeneralCommand.Default_Response
            ].schema(command_id=command_id, status=res[0].status)
        if command_id == DOWN_CLOSE:
            (res,) = await self.endpoint.multistate_output.write_attributes(
                {"present_value": 0}
            )
            return foundation.GENERAL_COMMANDS[
                foundation.GeneralCommand.Default_Response
            ].schema(command_id=command_id, status=res[0].status)
        if command_id == GO_TO_LIFT_PERCENTAGE:
            (res,) = await self.endpoint.analog_output.write_attributes(
                {"present_value": (100 - args[0])}
            )
            return foundation.GENERAL_COMMANDS[
                foundation.GeneralCommand.Default_Response
            ].schema(command_id=command_id, status=res[0].status)
        if command_id == STOP:
            (res,) = await self.endpoint.multistate_output.write_attributes(
                {"present_value": 2}
            )
            return foundation.GENERAL_COMMANDS[
                foundation.GeneralCommand.Default_Response
            ].schema(command_id=command_id, status=res[0].status)


class MultistateOutputRollerE1(CustomCluster, MultistateOutput):
    """Multistate Output cluster which overwrites present_value.
    Otherwise, it gives errors of wrong datatype when using it in the commands.
    """

    attributes = MultistateOutput.attributes.copy()
    attributes.update(
        {
            0x0055: ("present_value", t.uint16_t),
        }
    )


class PowerConfigurationRollerE1(PowerConfiguration, LocalDataCluster):
    """Xiaomi power configuration cluster implementation."""

    BATTERY_PERCENTAGE_REMAINING = 0x0021

    def __init__(self, *args: Any, **kwargs: Any) -> None:
        """Init."""
        super().__init__(*args, **kwargs)
        self.endpoint.device.power_bus_percentage.add_listener(self)

    def update_battery_percentage(self, value: int) -> None:
        """Doubles the battery percentage to the Zigbee spec's expected 200% maximum."""
        super()._update_attribute(
            self.BATTERY_PERCENTAGE_REMAINING,
            (value * 2),
        )


class RollerE1AQ(XiaomiCustomDevice):
    """Aqara Roller Shade Driver E1 device."""

    def __init__(self, *args: Any, **kwargs: Any) -> None:
        """Init."""
        self.power_bus_percentage: Bus = Bus()  # type: ignore
        super().__init__(*args, **kwargs)  # type: ignore

    signature = {
        MODELS_INFO: [(LUMI, "lumi.curtain.acn002")],
        ENDPOINTS: {
            # <SizePrefixedSimpleDescriptor endpoint=1 profile=260 device_type=256
            # device_version=1
            # input_clusters=[0, 2, 3, 4, 5, 6, 9, 64704, 13, 19, 258]
            # output_clusters=[10, 25]>
            1: {
                PROFILE_ID: zha.PROFILE_ID,
                DEVICE_TYPE: zha.DeviceType.ON_OFF_LIGHT,
                INPUT_CLUSTERS: [
                    Alarms.cluster_id,
                    AnalogOutput.cluster_id,
                    Basic.cluster_id,
                    DeviceTemperature.cluster_id,
                    Groups.cluster_id,
                    Identify.cluster_id,
                    XiaomiAqaraRollerE1.cluster_id,
                    MultistateOutput.cluster_id,
                    OnOff.cluster_id,
                    Scenes.cluster_id,
                    WindowCovering.cluster_id,
                ],
                OUTPUT_CLUSTERS: [
                    Ota.cluster_id,
                    Time.cluster_id,
                ],
            },
            # <SizePrefixedSimpleDescriptor endpoint=242 profile=41440 device_type=97
            # device_version=0,
            # input_clusters=[]
            # output_clusters=[33]>
            242: {
                PROFILE_ID: 41440,
                DEVICE_TYPE: 0x0061,
                INPUT_CLUSTERS: [],
                OUTPUT_CLUSTERS: [
                    GreenPowerProxy.cluster_id,
                ],
            },
        },
    }
    replacement = {
        ENDPOINTS: {
            1: {
                PROFILE_ID: zha.PROFILE_ID,
                DEVICE_TYPE: zha.DeviceType.WINDOW_COVERING_DEVICE,
                INPUT_CLUSTERS: [
                    Alarms.cluster_id,
                    AnalogOutputRollerE1,
                    BasicCluster,
                    DeviceTemperature.cluster_id,
                    Groups.cluster_id,
                    Identify.cluster_id,
                    XiaomiAqaraRollerE1,
                    MultistateOutputRollerE1,
                    Scenes.cluster_id,
                    WindowCoveringRollerE1,
                    PowerConfigurationRollerE1,
                ],
                OUTPUT_CLUSTERS: [
                    Ota.cluster_id,
                    Time.cluster_id,
                ],
            },
            242: {
                PROFILE_ID: 41440,
                DEVICE_TYPE: 0x0061,
                INPUT_CLUSTERS: [],
                OUTPUT_CLUSTERS: [
                    GreenPowerProxy.cluster_id,
                ],
            },
        },
    }


class RollerE1AQ_2(RollerE1AQ):
    """Aqara Roller Shade Driver E1 (version 2) device."""

    signature = {
        MODELS_INFO: [(LUMI, "lumi.curtain.acn002")],
        ENDPOINTS: {
            # <SizePrefixedSimpleDescriptor endpoint=1 profile=260 device_type=256
            # device_version=1
            # input_clusters=[0, 2, 3, 4, 5, 6, 9, 13, 19, 258]
            # output_clusters=[10, 25]>
            1: {
                PROFILE_ID: zha.PROFILE_ID,
                DEVICE_TYPE: zha.DeviceType.WINDOW_COVERING_DEVICE,
                INPUT_CLUSTERS: [
                    Alarms.cluster_id,
                    AnalogOutput.cluster_id,
                    Basic.cluster_id,
                    DeviceTemperature.cluster_id,
                    Groups.cluster_id,
                    Identify.cluster_id,
                    MultistateOutput.cluster_id,
                    OnOff.cluster_id,
                    Scenes.cluster_id,
                    WindowCovering.cluster_id,
                ],
                OUTPUT_CLUSTERS: [
                    Ota.cluster_id,
                    Time.cluster_id,
                ],
            },
            # <SizePrefixedSimpleDescriptor endpoint=242 profile=41440 device_type=97
            # device_version=0,
            # input_clusters=[]
            # output_clusters=[33]>
            242: {
                PROFILE_ID: 41440,
                DEVICE_TYPE: 0x0061,
                INPUT_CLUSTERS: [],
                OUTPUT_CLUSTERS: [
                    GreenPowerProxy.cluster_id,
                ],
            },
        },
    }

@TheJulianJES
Copy link
Member

TheJulianJES commented Oct 15, 2023

Yeah, there are multiple issues with that quirk.
Please try this one: zigpy/zha-device-handlers#2629

Ah, mhmm. You have another device. What doesn't work without the custom quirk?

(zigpy/zha-device-handlers#2620)

@TheJulianJES
Copy link
Member

Please also upload the device signature / device diagnostics without having the quirk applied.

@SGXander
Copy link
Author

To be fair it was a few months ago at the start of the year I got these so I can't honestly say I've tried without. I've just compared mine with the one in zigpy and it contains mine plus an _3 so I should probably just remove altogether. QQ: do I just remove the .py from the quirks folder and restart the ZHA integration or does HA need a full restart to remove a quirk?

@SGXander
Copy link
Author

Still having perf issues with hypervisor/VM so had to restart HA this morning anyway and can confirm the error is gone with no custom quirks file. For those with E1s it looks like the custom bits are now baked in to zigpy 👍.

@MattWestb
Copy link
Contributor

Its one more problem then you have only end device that need having one parent for working and you is only having the coordinator so you have one star network and not one redundant mesh network and then the coordinator is going off like all devices is loosing the network and is getting problems. Minimum is 3 routers for getting some mesh network functionality and redundancy.

@github-actions github-actions bot locked and limited conversation to collaborators Nov 16, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests

6 participants