diff --git a/homeassistant/components/zha/binary_sensor.py b/homeassistant/components/zha/binary_sensor.py index 9b057a3cbc3..5ec829fcb05 100644 --- a/homeassistant/components/zha/binary_sensor.py +++ b/homeassistant/components/zha/binary_sensor.py @@ -74,7 +74,7 @@ class BinarySensor(ZhaEntity, BinarySensorEntity): _attribute_name: str - def __init__(self, unique_id, zha_device, cluster_handlers, **kwargs): + def __init__(self, unique_id, zha_device, cluster_handlers, **kwargs) -> None: """Initialize the ZHA binary sensor.""" super().__init__(unique_id, zha_device, cluster_handlers, **kwargs) self._cluster_handler = cluster_handlers[0] @@ -336,3 +336,16 @@ class AqaraLinkageAlarmState(BinarySensor): _unique_id_suffix = "linkage_alarm_state" _attr_device_class: BinarySensorDeviceClass = BinarySensorDeviceClass.SMOKE _attr_translation_key: str = "linkage_alarm_state" + + +@CONFIG_DIAGNOSTIC_MATCH( + cluster_handler_names="opple_cluster", models={"lumi.curtain.agl001"} +) +class AqaraE1CurtainMotorOpenedByHandBinarySensor(BinarySensor): + """Opened by hand binary sensor.""" + + _unique_id_suffix = "hand_open" + _attribute_name = "hand_open" + _attr_translation_key = "hand_open" + _attr_icon = "mdi:hand-wave" + _attr_entity_category = EntityCategory.DIAGNOSTIC diff --git a/homeassistant/components/zha/core/cluster_handlers/general.py b/homeassistant/components/zha/core/cluster_handlers/general.py index 045e6a8f593..14401b260b2 100644 --- a/homeassistant/components/zha/core/cluster_handlers/general.py +++ b/homeassistant/components/zha/core/cluster_handlers/general.py @@ -203,6 +203,9 @@ class BasicClusterHandler(ClusterHandler): ): self.ZCL_INIT_ATTRS = self.ZCL_INIT_ATTRS.copy() self.ZCL_INIT_ATTRS["transmit_power"] = True + elif self.cluster.endpoint.model == "lumi.curtain.agl001": + self.ZCL_INIT_ATTRS = self.ZCL_INIT_ATTRS.copy() + self.ZCL_INIT_ATTRS["power_source"] = True @registries.ZIGBEE_CLUSTER_HANDLER_REGISTRY.register(BinaryInput.cluster_id) diff --git a/homeassistant/components/zha/core/cluster_handlers/manufacturerspecific.py b/homeassistant/components/zha/core/cluster_handlers/manufacturerspecific.py index 9375ecf60b1..608a256606f 100644 --- a/homeassistant/components/zha/core/cluster_handlers/manufacturerspecific.py +++ b/homeassistant/components/zha/core/cluster_handlers/manufacturerspecific.py @@ -160,6 +160,14 @@ class OppleRemoteClusterHandler(ClusterHandler): "startup_on_off": True, "decoupled_mode": True, } + elif self.cluster.endpoint.model == "lumi.curtain.agl001": + self.ZCL_INIT_ATTRS = { + "hooks_state": True, + "hooks_lock": True, + "positions_stored": True, + "light_level": True, + "hand_open": True, + } async def async_initialize_cluster_handler_specific(self, from_cache: bool) -> None: """Initialize cluster handler specific.""" diff --git a/homeassistant/components/zha/select.py b/homeassistant/components/zha/select.py index 58f2a608e47..3736858d599 100644 --- a/homeassistant/components/zha/select.py +++ b/homeassistant/components/zha/select.py @@ -471,25 +471,6 @@ class AqaraT2RelayDecoupledMode(ZCLEnumSelectEntity): _attr_translation_key: str = "decoupled_mode" -class AqaraE1ReverseDirection(types.enum8): - """Aqara curtain reversal.""" - - Normal = 0x00 - Inverted = 0x01 - - -@CONFIG_DIAGNOSTIC_MATCH( - cluster_handler_names="window_covering", models={"lumi.curtain.agl001"} -) -class AqaraCurtainMode(ZCLEnumSelectEntity): - """Representation of a ZHA curtain mode configuration entity.""" - - _unique_id_suffix = "window_covering_mode" - _attribute_name = "window_covering_mode" - _enum = AqaraE1ReverseDirection - _attr_translation_key: str = "window_covering_mode" - - class InovelliOutputMode(types.enum1): """Inovelli output mode.""" diff --git a/homeassistant/components/zha/sensor.py b/homeassistant/components/zha/sensor.py index d3c8fc0b29d..f4689460f93 100644 --- a/homeassistant/components/zha/sensor.py +++ b/homeassistant/components/zha/sensor.py @@ -10,6 +10,8 @@ import random from typing import TYPE_CHECKING, Any, Self from zigpy import types +from zigpy.zcl.clusters.closures import WindowCovering +from zigpy.zcl.clusters.general import Basic from homeassistant.components.climate import HVACAction from homeassistant.components.sensor import ( @@ -51,6 +53,7 @@ from .core import discovery from .core.const import ( CLUSTER_HANDLER_ANALOG_INPUT, CLUSTER_HANDLER_BASIC, + CLUSTER_HANDLER_COVER, CLUSTER_HANDLER_DEVICE_TEMPERATURE, CLUSTER_HANDLER_ELECTRICAL_MEASUREMENT, CLUSTER_HANDLER_HUMIDITY, @@ -1312,3 +1315,55 @@ class SetpointChangeSource(EnumSensor): _attr_icon: str = "mdi:thermostat" _attr_entity_category = EntityCategory.DIAGNOSTIC _enum = SetpointChangeSourceEnum + + +@CONFIG_DIAGNOSTIC_MATCH(cluster_handler_names=CLUSTER_HANDLER_COVER) +# pylint: disable-next=hass-invalid-inheritance # needs fixing +class WindowCoveringTypeSensor(EnumSensor): + """Sensor that displays the type of a cover device.""" + + _attribute_name: str = WindowCovering.AttributeDefs.window_covering_type.name + _enum = WindowCovering.WindowCoveringType + _unique_id_suffix: str = WindowCovering.AttributeDefs.window_covering_type.name + _attr_translation_key: str = WindowCovering.AttributeDefs.window_covering_type.name + _attr_entity_category = EntityCategory.DIAGNOSTIC + _attr_icon = "mdi:curtains" + + +@CONFIG_DIAGNOSTIC_MATCH( + cluster_handler_names=CLUSTER_HANDLER_BASIC, models={"lumi.curtain.agl001"} +) +# pylint: disable-next=hass-invalid-inheritance # needs fixing +class AqaraCurtainMotorPowerSourceSensor(EnumSensor): + """Sensor that displays the power source of the Aqara E1 curtain motor device.""" + + _attribute_name: str = Basic.AttributeDefs.power_source.name + _enum = Basic.PowerSource + _unique_id_suffix: str = Basic.AttributeDefs.power_source.name + _attr_translation_key: str = Basic.AttributeDefs.power_source.name + _attr_entity_category = EntityCategory.DIAGNOSTIC + _attr_icon = "mdi:battery-positive" + + +class AqaraE1HookState(types.enum8): + """Aqara hook state.""" + + Unlocked = 0x00 + Locked = 0x01 + Locking = 0x02 + Unlocking = 0x03 + + +@CONFIG_DIAGNOSTIC_MATCH( + cluster_handler_names="opple_cluster", models={"lumi.curtain.agl001"} +) +# pylint: disable-next=hass-invalid-inheritance # needs fixing +class AqaraCurtainHookStateSensor(EnumSensor): + """Representation of a ZHA curtain mode configuration entity.""" + + _attribute_name = "hooks_state" + _enum = AqaraE1HookState + _unique_id_suffix = "hooks_state" + _attr_translation_key: str = "hooks_state" + _attr_icon: str = "mdi:hook" + _attr_entity_category = EntityCategory.DIAGNOSTIC diff --git a/homeassistant/components/zha/strings.json b/homeassistant/components/zha/strings.json index 0c9ff765710..3db54712dee 100644 --- a/homeassistant/components/zha/strings.json +++ b/homeassistant/components/zha/strings.json @@ -566,6 +566,9 @@ }, "ias_zone": { "name": "IAS zone" + }, + "hand_open": { + "name": "Opened by hand" } }, "button": { @@ -896,6 +899,15 @@ }, "setpoint_change_source": { "name": "Setpoint change source" + }, + "power_source": { + "name": "Power source" + }, + "window_covering_type": { + "name": "Window covering type" + }, + "hooks_state": { + "name": "Hooks state" } }, "switch": { @@ -923,6 +935,9 @@ "inverted": { "name": "Inverted" }, + "hooks_locked": { + "name": "Hooks locked" + }, "smart_bulb_mode": { "name": "Smart bulb mode" }, diff --git a/homeassistant/components/zha/switch.py b/homeassistant/components/zha/switch.py index fe2a43f7334..afc73baca70 100644 --- a/homeassistant/components/zha/switch.py +++ b/homeassistant/components/zha/switch.py @@ -685,3 +685,15 @@ class WindowCoveringInversionSwitch(ZHASwitchConfigurationEntity): if send_command: await self._cluster_handler.write_attributes_safe({name: current_mode}) await self.async_update() + + +@CONFIG_DIAGNOSTIC_MATCH( + cluster_handler_names="opple_cluster", models={"lumi.curtain.agl001"} +) +class AqaraE1CurtainMotorHooksLockedSwitch(ZHASwitchConfigurationEntity): + """Representation of a switch that controls whether the curtain motor hooks are locked.""" + + _unique_id_suffix = "hooks_lock" + _attribute_name = "hooks_lock" + _attr_translation_key = "hooks_locked" + _attr_icon: str = "mdi:lock"