Raise HomeAssistantError in entity action calls in Nice G.O. (#126439)

This commit is contained in:
IceBotYT 2024-09-30 09:36:10 -04:00 committed by GitHub
parent 060268747c
commit 927813ab3b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 225 additions and 6 deletions

View File

@ -2,15 +2,20 @@
from typing import Any
from aiohttp import ClientError
from nice_go import ApiError
from homeassistant.components.cover import (
CoverDeviceClass,
CoverEntity,
CoverEntityFeature,
)
from homeassistant.core import HomeAssistant
from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from . import NiceGOConfigEntry
from .const import DOMAIN
from .entity import NiceGOEntity
PARALLEL_UPDATES = 1
@ -62,11 +67,25 @@ class NiceGOCoverEntity(NiceGOEntity, CoverEntity):
if self.is_closed:
return
await self.coordinator.api.close_barrier(self._device_id)
try:
await self.coordinator.api.close_barrier(self._device_id)
except (ApiError, ClientError) as err:
raise HomeAssistantError(
translation_domain=DOMAIN,
translation_key="close_cover_error",
translation_placeholders={"exception": str(err)},
) from err
async def async_open_cover(self, **kwargs: Any) -> None:
"""Open the garage door."""
if self.is_opened:
return
await self.coordinator.api.open_barrier(self._device_id)
try:
await self.coordinator.api.open_barrier(self._device_id)
except (ApiError, ClientError) as err:
raise HomeAssistantError(
translation_domain=DOMAIN,
translation_key="open_cover_error",
translation_placeholders={"exception": str(err)},
) from err

View File

@ -2,11 +2,16 @@
from typing import TYPE_CHECKING, Any
from aiohttp import ClientError
from nice_go import ApiError
from homeassistant.components.light import ColorMode, LightEntity
from homeassistant.core import HomeAssistant
from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from . import NiceGOConfigEntry
from .const import DOMAIN
from .entity import NiceGOEntity
@ -43,9 +48,23 @@ class NiceGOLightEntity(NiceGOEntity, LightEntity):
async def async_turn_on(self, **kwargs: Any) -> None:
"""Turn on the light."""
await self.coordinator.api.light_on(self._device_id)
try:
await self.coordinator.api.light_on(self._device_id)
except (ApiError, ClientError) as error:
raise HomeAssistantError(
translation_domain=DOMAIN,
translation_key="light_on_error",
translation_placeholders={"exception": str(error)},
) from error
async def async_turn_off(self, **kwargs: Any) -> None:
"""Turn off the light."""
await self.coordinator.api.light_off(self._device_id)
try:
await self.coordinator.api.light_off(self._device_id)
except (ApiError, ClientError) as error:
raise HomeAssistantError(
translation_domain=DOMAIN,
translation_key="light_off_error",
translation_placeholders={"exception": str(error)},
) from error

View File

@ -53,5 +53,25 @@
"title": "Firmware update required",
"description": "Your device ({device_name}) requires a firmware update on the Nice G.O. app in order to work with this integration. Please update the firmware on the Nice G.O. app and reconfigure this integration."
}
},
"exceptions": {
"close_cover_error": {
"message": "Error closing the barrier: {exception}"
},
"open_cover_error": {
"message": "Error opening the barrier: {exception}"
},
"light_on_error": {
"message": "Error while turning on the light: {exception}"
},
"light_off_error": {
"message": "Error while turning off the light: {exception}"
},
"switch_on_error": {
"message": "Error while turning on the switch: {exception}"
},
"switch_off_error": {
"message": "Error while turning off the switch: {exception}"
}
}
}

View File

@ -5,11 +5,16 @@ from __future__ import annotations
import logging
from typing import Any
from aiohttp import ClientError
from nice_go import ApiError
from homeassistant.components.switch import SwitchDeviceClass, SwitchEntity
from homeassistant.core import HomeAssistant
from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from . import NiceGOConfigEntry
from .const import DOMAIN
from .entity import NiceGOEntity
_LOGGER = logging.getLogger(__name__)
@ -42,8 +47,24 @@ class NiceGOSwitchEntity(NiceGOEntity, SwitchEntity):
async def async_turn_on(self, **kwargs: Any) -> None:
"""Turn the switch on."""
await self.coordinator.api.vacation_mode_on(self.data.id)
try:
await self.coordinator.api.vacation_mode_on(self.data.id)
except (ApiError, ClientError) as error:
raise HomeAssistantError(
translation_domain=DOMAIN,
translation_key="switch_on_error",
translation_placeholders={"exception": str(error)},
) from error
async def async_turn_off(self, **kwargs: Any) -> None:
"""Turn the switch off."""
await self.coordinator.api.vacation_mode_off(self.data.id)
try:
await self.coordinator.api.vacation_mode_off(self.data.id)
except (ApiError, ClientError) as error:
raise HomeAssistantError(
translation_domain=DOMAIN,
translation_key="switch_off_error",
translation_placeholders={"exception": str(error)},
) from error

View File

@ -2,7 +2,10 @@
from unittest.mock import AsyncMock
from aiohttp import ClientError
from freezegun.api import FrozenDateTimeFactory
from nice_go import ApiError
import pytest
from syrupy import SnapshotAssertion
from homeassistant.components.cover import (
@ -20,6 +23,7 @@ from homeassistant.const import (
Platform,
)
from homeassistant.core import HomeAssistant
from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers import entity_registry as er
from . import setup_integration
@ -113,3 +117,46 @@ async def test_update_cover_state(
assert hass.states.get("cover.test_garage_1").state == STATE_OPENING
assert hass.states.get("cover.test_garage_2").state == STATE_CLOSING
@pytest.mark.parametrize(
("action", "error", "entity_id", "expected_error"),
[
(
SERVICE_OPEN_COVER,
ApiError,
"cover.test_garage_1",
"Error opening the barrier",
),
(
SERVICE_CLOSE_COVER,
ClientError,
"cover.test_garage_2",
"Error closing the barrier",
),
],
)
async def test_cover_exceptions(
hass: HomeAssistant,
mock_nice_go: AsyncMock,
mock_config_entry: MockConfigEntry,
freezer: FrozenDateTimeFactory,
action: str,
error: Exception,
entity_id: str,
expected_error: str,
) -> None:
"""Test that closing the cover works as intended."""
await setup_integration(hass, mock_config_entry, [Platform.COVER])
mock_nice_go.open_barrier.side_effect = error
mock_nice_go.close_barrier.side_effect = error
with pytest.raises(HomeAssistantError, match=expected_error):
await hass.services.async_call(
COVER_DOMAIN,
action,
{ATTR_ENTITY_ID: entity_id},
blocking=True,
)

View File

@ -2,6 +2,9 @@
from unittest.mock import AsyncMock
from aiohttp import ClientError
from nice_go import ApiError
import pytest
from syrupy import SnapshotAssertion
from homeassistant.components.light import (
@ -12,6 +15,7 @@ from homeassistant.components.light import (
from homeassistant.components.nice_go.const import DOMAIN
from homeassistant.const import ATTR_ENTITY_ID, STATE_OFF, STATE_ON, Platform
from homeassistant.core import HomeAssistant
from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers import entity_registry as er
from . import setup_integration
@ -88,3 +92,45 @@ async def test_update_light_state(
assert hass.states.get("light.test_garage_1_light").state == STATE_OFF
assert hass.states.get("light.test_garage_2_light").state == STATE_ON
assert hass.states.get("light.test_garage_3_light") is None
@pytest.mark.parametrize(
("action", "error", "entity_id", "expected_error"),
[
(
SERVICE_TURN_OFF,
ApiError,
"light.test_garage_1_light",
"Error while turning off the light",
),
(
SERVICE_TURN_ON,
ClientError,
"light.test_garage_2_light",
"Error while turning on the light",
),
],
)
async def test_error(
hass: HomeAssistant,
mock_nice_go: AsyncMock,
mock_config_entry: MockConfigEntry,
action: str,
error: Exception,
entity_id: str,
expected_error: str,
) -> None:
"""Test that errors are handled appropriately."""
await setup_integration(hass, mock_config_entry, [Platform.LIGHT])
mock_nice_go.light_on.side_effect = error
mock_nice_go.light_off.side_effect = error
with pytest.raises(HomeAssistantError, match=expected_error):
await hass.services.async_call(
LIGHT_DOMAIN,
action,
{ATTR_ENTITY_ID: entity_id},
blocking=True,
)

View File

@ -2,6 +2,10 @@
from unittest.mock import AsyncMock
from aiohttp import ClientError
from nice_go import ApiError
import pytest
from homeassistant.components.switch import (
DOMAIN as SWITCH_DOMAIN,
SERVICE_TURN_OFF,
@ -9,6 +13,7 @@ from homeassistant.components.switch import (
)
from homeassistant.const import ATTR_ENTITY_ID, Platform
from homeassistant.core import HomeAssistant
from homeassistant.exceptions import HomeAssistantError
from . import setup_integration
@ -41,3 +46,45 @@ async def test_turn_off(
blocking=True,
)
mock_nice_go.vacation_mode_off.assert_called_once_with("2")
@pytest.mark.parametrize(
("action", "error", "entity_id", "expected_error"),
[
(
SERVICE_TURN_OFF,
ApiError,
"switch.test_garage_1_vacation_mode",
"Error while turning off the switch",
),
(
SERVICE_TURN_ON,
ClientError,
"switch.test_garage_2_vacation_mode",
"Error while turning on the switch",
),
],
)
async def test_error(
hass: HomeAssistant,
mock_nice_go: AsyncMock,
mock_config_entry: MockConfigEntry,
action: str,
error: Exception,
entity_id: str,
expected_error: str,
) -> None:
"""Test that errors are handled appropriately."""
await setup_integration(hass, mock_config_entry, [Platform.SWITCH])
mock_nice_go.vacation_mode_on.side_effect = error
mock_nice_go.vacation_mode_off.side_effect = error
with pytest.raises(HomeAssistantError, match=expected_error):
await hass.services.async_call(
SWITCH_DOMAIN,
action,
{ATTR_ENTITY_ID: entity_id},
blocking=True,
)