From 07c304125aec142087348f2937ef73946e381741 Mon Sep 17 00:00:00 2001 From: Arie Catsman <120491684+catsmanac@users.noreply.github.com> Date: Thu, 13 Feb 2025 09:37:52 +0100 Subject: [PATCH] Add error handling to enphase_envoy select platform action (#136698) * Add error handling to enphase_envoy select platform action * Add translation key parameter to exception_handler decorator --- .../components/enphase_envoy/select.py | 4 +- tests/components/enphase_envoy/test_select.py | 80 +++++++++++++++++++ 2 files changed, 83 insertions(+), 1 deletion(-) diff --git a/homeassistant/components/enphase_envoy/select.py b/homeassistant/components/enphase_envoy/select.py index 546470a19d5..42b47e5d793 100644 --- a/homeassistant/components/enphase_envoy/select.py +++ b/homeassistant/components/enphase_envoy/select.py @@ -18,7 +18,7 @@ from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback from .const import DOMAIN from .coordinator import EnphaseConfigEntry, EnphaseUpdateCoordinator -from .entity import EnvoyBaseEntity +from .entity import EnvoyBaseEntity, exception_handler PARALLEL_UPDATES = 1 @@ -192,6 +192,7 @@ class EnvoyRelaySelectEntity(EnvoyBaseEntity, SelectEntity): """Return the state of the Enpower switch.""" return self.entity_description.value_fn(self.relay) + @exception_handler async def async_select_option(self, option: str) -> None: """Update the relay.""" await self.entity_description.update_fn(self.envoy, self.relay, option) @@ -243,6 +244,7 @@ class EnvoyStorageSettingsSelectEntity(EnvoyBaseEntity, SelectEntity): assert self.data.tariff.storage_settings is not None return self.entity_description.value_fn(self.data.tariff.storage_settings) + @exception_handler async def async_select_option(self, option: str) -> None: """Update the relay.""" await self.entity_description.update_fn(self.envoy, option) diff --git a/tests/components/enphase_envoy/test_select.py b/tests/components/enphase_envoy/test_select.py index e13492c7f54..a81a06a3441 100644 --- a/tests/components/enphase_envoy/test_select.py +++ b/tests/components/enphase_envoy/test_select.py @@ -2,6 +2,7 @@ from unittest.mock import AsyncMock, patch +from pyenphase.exceptions import EnvoyError import pytest from syrupy.assertion import SnapshotAssertion @@ -17,6 +18,7 @@ from homeassistant.components.enphase_envoy.select import ( from homeassistant.components.select import ATTR_OPTION, DOMAIN as SELECT_DOMAIN from homeassistant.const import ATTR_ENTITY_ID, SERVICE_SELECT_OPTION from homeassistant.core import HomeAssistant +from homeassistant.exceptions import HomeAssistantError from homeassistant.helpers import entity_registry as er from . import setup_integration @@ -157,6 +159,46 @@ async def test_select_relay_modes( ) +@pytest.mark.parametrize( + ("mock_envoy", "relay", "target", "action"), + [("envoy_metered_batt_relay", "NC1", "generator_action", "powered")], + indirect=["mock_envoy"], +) +async def test_update_dry_contact_actions_with_error( + hass: HomeAssistant, + mock_envoy: AsyncMock, + config_entry: MockConfigEntry, + target: str, + relay: str, + action: str, +) -> None: + """Test select platform update dry contact action with error return.""" + with patch("homeassistant.components.enphase_envoy.PLATFORMS", [Platform.SELECT]): + await setup_integration(hass, config_entry) + + entity_base = f"{Platform.SELECT}." + + assert (dry_contact := mock_envoy.data.dry_contact_settings[relay]) + assert (name := dry_contact.load_name.lower().replace(" ", "_")) + + test_entity = f"{entity_base}{name}_{target}" + + mock_envoy.update_dry_contact.side_effect = EnvoyError("Test") + with pytest.raises( + HomeAssistantError, + match=f"Failed to execute async_select_option for {test_entity}, host", + ): + await hass.services.async_call( + SELECT_DOMAIN, + SERVICE_SELECT_OPTION, + { + ATTR_ENTITY_ID: test_entity, + ATTR_OPTION: action, + }, + blocking=True, + ) + + @pytest.mark.parametrize( ("mock_envoy", "use_serial"), [ @@ -197,6 +239,44 @@ async def test_select_storage_modes( mock_envoy.set_storage_mode.assert_called_once_with(REVERSE_STORAGE_MODE_MAP[mode]) +@pytest.mark.parametrize( + ("mock_envoy", "use_serial"), + [ + ("envoy_metered_batt_relay", "enpower_654321"), + ("envoy_eu_batt", "envoy_1234"), + ], + indirect=["mock_envoy"], +) +@pytest.mark.parametrize(("mode"), ["backup"]) +async def test_set_storage_modes_with_error( + hass: HomeAssistant, + mock_envoy: AsyncMock, + config_entry: MockConfigEntry, + use_serial: str, + mode: str, +) -> None: + """Test select platform set storage mode with error return.""" + with patch("homeassistant.components.enphase_envoy.PLATFORMS", [Platform.SELECT]): + await setup_integration(hass, config_entry) + + test_entity = f"{Platform.SELECT}.{use_serial}_storage_mode" + + mock_envoy.set_storage_mode.side_effect = EnvoyError("Test") + with pytest.raises( + HomeAssistantError, + match=f"Failed to execute async_select_option for {test_entity}, host", + ): + await hass.services.async_call( + SELECT_DOMAIN, + SERVICE_SELECT_OPTION, + { + ATTR_ENTITY_ID: test_entity, + ATTR_OPTION: mode, + }, + blocking=True, + ) + + @pytest.mark.parametrize( ("mock_envoy", "use_serial"), [