From 758e7489f1f2f90f9447d97b9c95dad9b902bb6a Mon Sep 17 00:00:00 2001 From: puddly <32534428+puddly@users.noreply.github.com> Date: Tue, 30 Jan 2024 15:51:38 -0500 Subject: [PATCH] Fix ZHA cover inversion handling missing attributes (#109151) * Allow `window_covering_type` to be `None` * Create a `window_covering_mode` attribute and simplify inversion switch * Revert "Create a `window_covering_mode` attribute and simplify inversion switch" This reverts commit 048d649b4dff20aff2a0baa81cfdb9c7d3ce71c6. * check both config status and mode * coverage --------- Co-authored-by: David Mulcahey --- .../zha/core/cluster_handlers/closures.py | 6 ++-- homeassistant/components/zha/switch.py | 36 +++++++++++++++++++ tests/components/zha/test_switch.py | 30 ++++++++++++++++ 3 files changed, 68 insertions(+), 4 deletions(-) diff --git a/homeassistant/components/zha/core/cluster_handlers/closures.py b/homeassistant/components/zha/core/cluster_handlers/closures.py index 13ca6f92aaf..879765aec3c 100644 --- a/homeassistant/components/zha/core/cluster_handlers/closures.py +++ b/homeassistant/components/zha/core/cluster_handlers/closures.py @@ -267,8 +267,6 @@ class WindowCoveringClusterHandler(ClusterHandler): ) @property - def window_covering_type(self) -> WindowCovering.WindowCoveringType: + def window_covering_type(self) -> WindowCovering.WindowCoveringType | None: """Return the window covering type.""" - return WindowCovering.WindowCoveringType( - self.cluster.get(WindowCovering.AttributeDefs.window_covering_type.name) - ) + return self.cluster.get(WindowCovering.AttributeDefs.window_covering_type.name) diff --git a/homeassistant/components/zha/switch.py b/homeassistant/components/zha/switch.py index 57b84bd1aa1..fe2a43f7334 100644 --- a/homeassistant/components/zha/switch.py +++ b/homeassistant/components/zha/switch.py @@ -604,6 +604,42 @@ class WindowCoveringInversionSwitch(ZHASwitchConfigurationEntity): _attr_translation_key = "inverted" _attr_icon: str = "mdi:arrow-up-down" + @classmethod + def create_entity( + cls, + unique_id: str, + zha_device: ZHADevice, + cluster_handlers: list[ClusterHandler], + **kwargs: Any, + ) -> Self | None: + """Entity Factory. + + Return entity if it is a supported configuration, otherwise return None + """ + cluster_handler = cluster_handlers[0] + window_covering_mode_attr = ( + WindowCovering.AttributeDefs.window_covering_mode.name + ) + # this entity needs 2 attributes to function + if ( + cls._attribute_name in cluster_handler.cluster.unsupported_attributes + or cls._attribute_name not in cluster_handler.cluster.attributes_by_name + or cluster_handler.cluster.get(cls._attribute_name) is None + or window_covering_mode_attr + in cluster_handler.cluster.unsupported_attributes + or window_covering_mode_attr + not in cluster_handler.cluster.attributes_by_name + or cluster_handler.cluster.get(window_covering_mode_attr) is None + ): + _LOGGER.debug( + "%s is not supported - skipping %s entity creation", + cls._attribute_name, + cls.__name__, + ) + return None + + return cls(unique_id, zha_device, cluster_handlers, **kwargs) + @property def is_on(self) -> bool: """Return if the switch is on based on the statemachine.""" diff --git a/tests/components/zha/test_switch.py b/tests/components/zha/test_switch.py index cd25d17f84f..6bfd7e051f1 100644 --- a/tests/components/zha/test_switch.py +++ b/tests/components/zha/test_switch.py @@ -684,3 +684,33 @@ async def test_cover_inversion_switch( state = hass.states.get(entity_id) assert state assert state.state == STATE_OFF + + +async def test_cover_inversion_switch_not_created( + hass: HomeAssistant, zha_device_joined_restored, zigpy_cover_device +) -> None: + """Test ZHA cover platform.""" + + # load up cover domain + cluster = zigpy_cover_device.endpoints[1].window_covering + cluster.PLUGGED_ATTR_READS = { + WCAttrs.current_position_lift_percentage.name: 65, + WCAttrs.current_position_tilt_percentage.name: 42, + WCAttrs.config_status.name: WCCS(~WCCS.Open_up_commands_reversed), + } + update_attribute_cache(cluster) + zha_device = await zha_device_joined_restored(zigpy_cover_device) + + assert cluster.read_attributes.call_count == 3 + assert ( + WCAttrs.current_position_lift_percentage.name + in cluster.read_attributes.call_args[0][0] + ) + assert ( + WCAttrs.current_position_tilt_percentage.name + in cluster.read_attributes.call_args[0][0] + ) + + # entity should not be created when mode or config status aren't present + entity_id = find_entity_id(Platform.SWITCH, zha_device, hass) + assert entity_id is None