mirror of
https://github.com/home-assistant/core.git
synced 2025-07-13 16:27:08 +00:00
Add proper exception handling to lamarzocco (#125913)
This commit is contained in:
parent
57e041171b
commit
2e1732fadf
@ -4,10 +4,12 @@ from collections.abc import Callable, Coroutine
|
||||
from dataclasses import dataclass
|
||||
from typing import Any
|
||||
|
||||
from lmcloud.exceptions import RequestNotSuccessful
|
||||
from lmcloud.lm_machine import LaMarzoccoMachine
|
||||
|
||||
from homeassistant.components.button import ButtonEntity, ButtonEntityDescription
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.exceptions import HomeAssistantError
|
||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||
|
||||
from . import LaMarzoccoConfigEntry
|
||||
@ -55,5 +57,13 @@ class LaMarzoccoButtonEntity(LaMarzoccoEntity, ButtonEntity):
|
||||
|
||||
async def async_press(self) -> None:
|
||||
"""Press button."""
|
||||
await self.entity_description.press_fn(self.coordinator.device)
|
||||
try:
|
||||
await self.entity_description.press_fn(self.coordinator.device)
|
||||
except RequestNotSuccessful as exc:
|
||||
raise HomeAssistantError(
|
||||
translation_key="button_error",
|
||||
translation_placeholders={
|
||||
"key": self.entity_description.key,
|
||||
},
|
||||
) from exc
|
||||
await self.coordinator.async_request_refresh()
|
||||
|
@ -11,6 +11,7 @@ from lmcloud.const import (
|
||||
PhysicalKey,
|
||||
PrebrewMode,
|
||||
)
|
||||
from lmcloud.exceptions import RequestNotSuccessful
|
||||
from lmcloud.lm_machine import LaMarzoccoMachine
|
||||
from lmcloud.models import LaMarzoccoMachineConfig
|
||||
|
||||
@ -27,6 +28,7 @@ from homeassistant.const import (
|
||||
UnitOfTime,
|
||||
)
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.exceptions import HomeAssistantError
|
||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||
|
||||
from . import LaMarzoccoConfigEntry
|
||||
@ -220,7 +222,18 @@ class LaMarzoccoNumberEntity(LaMarzoccoEntity, NumberEntity):
|
||||
async def async_set_native_value(self, value: float) -> None:
|
||||
"""Set the value."""
|
||||
if value != self.native_value:
|
||||
await self.entity_description.set_value_fn(self.coordinator.device, value)
|
||||
try:
|
||||
await self.entity_description.set_value_fn(
|
||||
self.coordinator.device, value
|
||||
)
|
||||
except RequestNotSuccessful as exc:
|
||||
raise HomeAssistantError(
|
||||
translation_key="number_exception",
|
||||
translation_placeholders={
|
||||
"key": self.entity_description.key,
|
||||
"value": str(value),
|
||||
},
|
||||
) from exc
|
||||
self.async_write_ha_state()
|
||||
|
||||
|
||||
@ -258,7 +271,17 @@ class LaMarzoccoKeyNumberEntity(LaMarzoccoEntity, NumberEntity):
|
||||
async def async_set_native_value(self, value: float) -> None:
|
||||
"""Set the value."""
|
||||
if value != self.native_value:
|
||||
await self.entity_description.set_value_fn(
|
||||
self.coordinator.device, value, PhysicalKey(self.pyhsical_key)
|
||||
)
|
||||
try:
|
||||
await self.entity_description.set_value_fn(
|
||||
self.coordinator.device, value, PhysicalKey(self.pyhsical_key)
|
||||
)
|
||||
except RequestNotSuccessful as exc:
|
||||
raise HomeAssistantError(
|
||||
translation_key="number_exception_key",
|
||||
translation_placeholders={
|
||||
"key": self.entity_description.key,
|
||||
"value": str(value),
|
||||
"physical_key": str(self.pyhsical_key),
|
||||
},
|
||||
) from exc
|
||||
self.async_write_ha_state()
|
||||
|
@ -5,12 +5,14 @@ from dataclasses import dataclass
|
||||
from typing import Any
|
||||
|
||||
from lmcloud.const import MachineModel, PrebrewMode, SteamLevel
|
||||
from lmcloud.exceptions import RequestNotSuccessful
|
||||
from lmcloud.lm_machine import LaMarzoccoMachine
|
||||
from lmcloud.models import LaMarzoccoMachineConfig
|
||||
|
||||
from homeassistant.components.select import SelectEntity, SelectEntityDescription
|
||||
from homeassistant.const import EntityCategory
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.exceptions import HomeAssistantError
|
||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||
|
||||
from . import LaMarzoccoConfigEntry
|
||||
@ -113,7 +115,16 @@ class LaMarzoccoSelectEntity(LaMarzoccoEntity, SelectEntity):
|
||||
async def async_select_option(self, option: str) -> None:
|
||||
"""Change the selected option."""
|
||||
if option != self.current_option:
|
||||
await self.entity_description.select_option_fn(
|
||||
self.coordinator.device, option
|
||||
)
|
||||
try:
|
||||
await self.entity_description.select_option_fn(
|
||||
self.coordinator.device, option
|
||||
)
|
||||
except RequestNotSuccessful as exc:
|
||||
raise HomeAssistantError(
|
||||
translation_key="select_option_error",
|
||||
translation_placeholders={
|
||||
"key": self.entity_description.key,
|
||||
"option": option,
|
||||
},
|
||||
) from exc
|
||||
self.async_write_ha_state()
|
||||
|
@ -180,5 +180,31 @@
|
||||
"title": "Unsupported gateway firmware",
|
||||
"description": "Gateway firmware {gateway_version} is no longer supported by this integration, please update."
|
||||
}
|
||||
},
|
||||
"exceptions": {
|
||||
"auto_on_off_error": {
|
||||
"message": "Error while setting auto on/off to {state} for {id}"
|
||||
},
|
||||
"button_error": {
|
||||
"message": "Error while executing button {key}"
|
||||
},
|
||||
"number_exception": {
|
||||
"message": "Error while setting value {value} for number {key}"
|
||||
},
|
||||
"number_exception_key": {
|
||||
"message": "Error while setting value {value} for number {key}, key {physical_key}"
|
||||
},
|
||||
"select_option_error": {
|
||||
"message": "Error while setting select option {option} for {key}"
|
||||
},
|
||||
"switch_on_error": {
|
||||
"message": "Error while turning on switch {key}"
|
||||
},
|
||||
"switch_off_error": {
|
||||
"message": "Error while turning off switch {key}"
|
||||
},
|
||||
"update_failed": {
|
||||
"message": "Error while updating {key}"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -5,12 +5,14 @@ from dataclasses import dataclass
|
||||
from typing import Any
|
||||
|
||||
from lmcloud.const import BoilerType
|
||||
from lmcloud.exceptions import RequestNotSuccessful
|
||||
from lmcloud.lm_machine import LaMarzoccoMachine
|
||||
from lmcloud.models import LaMarzoccoMachineConfig
|
||||
|
||||
from homeassistant.components.switch import SwitchEntity, SwitchEntityDescription
|
||||
from homeassistant.const import EntityCategory
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.exceptions import HomeAssistantError
|
||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||
|
||||
from . import LaMarzoccoConfigEntry
|
||||
@ -77,12 +79,24 @@ class LaMarzoccoSwitchEntity(LaMarzoccoEntity, SwitchEntity):
|
||||
|
||||
async def async_turn_on(self, **kwargs: Any) -> None:
|
||||
"""Turn device on."""
|
||||
await self.entity_description.control_fn(self.coordinator.device, True)
|
||||
try:
|
||||
await self.entity_description.control_fn(self.coordinator.device, True)
|
||||
except RequestNotSuccessful as exc:
|
||||
raise HomeAssistantError(
|
||||
translation_key="switch_on_error",
|
||||
translation_placeholders={"key": self.entity_description.key},
|
||||
) from exc
|
||||
self.async_write_ha_state()
|
||||
|
||||
async def async_turn_off(self, **kwargs: Any) -> None:
|
||||
"""Turn device off."""
|
||||
await self.entity_description.control_fn(self.coordinator.device, False)
|
||||
try:
|
||||
await self.entity_description.control_fn(self.coordinator.device, False)
|
||||
except RequestNotSuccessful as exc:
|
||||
raise HomeAssistantError(
|
||||
translation_key="switch_off_error",
|
||||
translation_placeholders={"name": self.entity_description.key},
|
||||
) from exc
|
||||
self.async_write_ha_state()
|
||||
|
||||
@property
|
||||
@ -114,7 +128,13 @@ class LaMarzoccoAutoOnOffSwitchEntity(LaMarzoccoBaseEntity, SwitchEntity):
|
||||
self._identifier
|
||||
]
|
||||
wake_up_sleep_entry.enabled = state
|
||||
await self.coordinator.device.set_wake_up_sleep(wake_up_sleep_entry)
|
||||
try:
|
||||
await self.coordinator.device.set_wake_up_sleep(wake_up_sleep_entry)
|
||||
except RequestNotSuccessful as exc:
|
||||
raise HomeAssistantError(
|
||||
translation_key="auto_on_off_error",
|
||||
translation_placeholders={"id": self._identifier, "state": str(state)},
|
||||
) from exc
|
||||
self.async_write_ha_state()
|
||||
|
||||
async def async_turn_on(self, **kwargs: Any) -> None:
|
||||
|
@ -4,6 +4,7 @@ from dataclasses import dataclass
|
||||
from typing import Any
|
||||
|
||||
from lmcloud.const import FirmwareType
|
||||
from lmcloud.exceptions import RequestNotSuccessful
|
||||
|
||||
from homeassistant.components.update import (
|
||||
UpdateDeviceClass,
|
||||
@ -94,10 +95,23 @@ class LaMarzoccoUpdateEntity(LaMarzoccoEntity, UpdateEntity):
|
||||
"""Install an update."""
|
||||
self._attr_in_progress = True
|
||||
self.async_write_ha_state()
|
||||
success = await self.coordinator.device.update_firmware(
|
||||
self.entity_description.component
|
||||
)
|
||||
try:
|
||||
success = await self.coordinator.device.update_firmware(
|
||||
self.entity_description.component
|
||||
)
|
||||
except RequestNotSuccessful as exc:
|
||||
raise HomeAssistantError(
|
||||
translation_key="update_failed",
|
||||
translation_placeholders={
|
||||
"key": self.entity_description.key,
|
||||
},
|
||||
) from exc
|
||||
if not success:
|
||||
raise HomeAssistantError("Update failed")
|
||||
raise HomeAssistantError(
|
||||
translation_key="update_failed",
|
||||
translation_placeholders={
|
||||
"key": self.entity_description.key,
|
||||
},
|
||||
)
|
||||
self._attr_in_progress = False
|
||||
await self.coordinator.async_request_refresh()
|
||||
|
@ -2,12 +2,14 @@
|
||||
|
||||
from unittest.mock import MagicMock
|
||||
|
||||
from lmcloud.exceptions import RequestNotSuccessful
|
||||
import pytest
|
||||
from syrupy import SnapshotAssertion
|
||||
|
||||
from homeassistant.components.button import DOMAIN as BUTTON_DOMAIN, SERVICE_PRESS
|
||||
from homeassistant.const import ATTR_ENTITY_ID
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.exceptions import HomeAssistantError
|
||||
from homeassistant.helpers import entity_registry as er
|
||||
|
||||
pytestmark = pytest.mark.usefixtures("init_integration")
|
||||
@ -42,3 +44,26 @@ async def test_start_backflush(
|
||||
|
||||
assert len(mock_lamarzocco.start_backflush.mock_calls) == 1
|
||||
mock_lamarzocco.start_backflush.assert_called_once()
|
||||
|
||||
|
||||
async def test_button_error(
|
||||
hass: HomeAssistant,
|
||||
mock_lamarzocco: MagicMock,
|
||||
) -> None:
|
||||
"""Test the La Marzocco button error."""
|
||||
serial_number = mock_lamarzocco.serial_number
|
||||
|
||||
state = hass.states.get(f"button.{serial_number}_start_backflush")
|
||||
assert state
|
||||
|
||||
mock_lamarzocco.start_backflush.side_effect = RequestNotSuccessful("Boom.")
|
||||
with pytest.raises(HomeAssistantError) as exc_info:
|
||||
await hass.services.async_call(
|
||||
BUTTON_DOMAIN,
|
||||
SERVICE_PRESS,
|
||||
{
|
||||
ATTR_ENTITY_ID: f"button.{serial_number}_start_backflush",
|
||||
},
|
||||
blocking=True,
|
||||
)
|
||||
assert exc_info.value.translation_key == "button_error"
|
||||
|
@ -9,6 +9,7 @@ from lmcloud.const import (
|
||||
PhysicalKey,
|
||||
PrebrewMode,
|
||||
)
|
||||
from lmcloud.exceptions import RequestNotSuccessful
|
||||
import pytest
|
||||
from syrupy import SnapshotAssertion
|
||||
|
||||
@ -19,6 +20,7 @@ from homeassistant.components.number import (
|
||||
)
|
||||
from homeassistant.const import ATTR_ENTITY_ID, STATE_UNAVAILABLE
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.exceptions import HomeAssistantError
|
||||
from homeassistant.helpers import device_registry as dr, entity_registry as er
|
||||
|
||||
from . import async_init_integration
|
||||
@ -379,3 +381,46 @@ async def test_not_existing_key_entities(
|
||||
for key in range(1, KEYS_PER_MODEL[MachineModel.GS3_AV] + 1):
|
||||
state = hass.states.get(f"number.{serial_number}_{entity}_key_{key}")
|
||||
assert state is None
|
||||
|
||||
|
||||
@pytest.mark.usefixtures("entity_registry_enabled_by_default")
|
||||
async def test_number_error(
|
||||
hass: HomeAssistant,
|
||||
mock_lamarzocco: MagicMock,
|
||||
mock_config_entry: MockConfigEntry,
|
||||
) -> None:
|
||||
"""Test number entities raise error on service call."""
|
||||
await async_init_integration(hass, mock_config_entry)
|
||||
serial_number = mock_lamarzocco.serial_number
|
||||
|
||||
state = hass.states.get(f"number.{serial_number}_coffee_target_temperature")
|
||||
assert state
|
||||
|
||||
mock_lamarzocco.set_temp.side_effect = RequestNotSuccessful("Boom")
|
||||
with pytest.raises(HomeAssistantError) as exc_info:
|
||||
await hass.services.async_call(
|
||||
NUMBER_DOMAIN,
|
||||
SERVICE_SET_VALUE,
|
||||
{
|
||||
ATTR_ENTITY_ID: f"number.{serial_number}_coffee_target_temperature",
|
||||
ATTR_VALUE: 94,
|
||||
},
|
||||
blocking=True,
|
||||
)
|
||||
assert exc_info.value.translation_key == "number_exception"
|
||||
|
||||
state = hass.states.get(f"number.{serial_number}_dose_key_1")
|
||||
assert state
|
||||
|
||||
mock_lamarzocco.set_dose.side_effect = RequestNotSuccessful("Boom")
|
||||
with pytest.raises(HomeAssistantError) as exc_info:
|
||||
await hass.services.async_call(
|
||||
NUMBER_DOMAIN,
|
||||
SERVICE_SET_VALUE,
|
||||
{
|
||||
ATTR_ENTITY_ID: f"number.{serial_number}_dose_key_1",
|
||||
ATTR_VALUE: 99,
|
||||
},
|
||||
blocking=True,
|
||||
)
|
||||
assert exc_info.value.translation_key == "number_exception_key"
|
||||
|
@ -3,6 +3,7 @@
|
||||
from unittest.mock import MagicMock
|
||||
|
||||
from lmcloud.const import MachineModel, PrebrewMode, SteamLevel
|
||||
from lmcloud.exceptions import RequestNotSuccessful
|
||||
import pytest
|
||||
from syrupy import SnapshotAssertion
|
||||
|
||||
@ -13,6 +14,7 @@ from homeassistant.components.select import (
|
||||
)
|
||||
from homeassistant.const import ATTR_ENTITY_ID
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.exceptions import HomeAssistantError
|
||||
from homeassistant.helpers import entity_registry as er
|
||||
|
||||
pytestmark = pytest.mark.usefixtures("init_integration")
|
||||
@ -117,3 +119,29 @@ async def test_pre_brew_infusion_select_none(
|
||||
state = hass.states.get(f"select.{serial_number}_prebrew_infusion_mode")
|
||||
|
||||
assert state is None
|
||||
|
||||
|
||||
async def test_select_errors(
|
||||
hass: HomeAssistant,
|
||||
mock_lamarzocco: MagicMock,
|
||||
) -> None:
|
||||
"""Test select errors."""
|
||||
serial_number = mock_lamarzocco.serial_number
|
||||
|
||||
state = hass.states.get(f"select.{serial_number}_prebrew_infusion_mode")
|
||||
assert state
|
||||
|
||||
mock_lamarzocco.set_prebrew_mode.side_effect = RequestNotSuccessful("Boom")
|
||||
|
||||
# Test setting invalid option
|
||||
with pytest.raises(HomeAssistantError) as exc_info:
|
||||
await hass.services.async_call(
|
||||
SELECT_DOMAIN,
|
||||
SERVICE_SELECT_OPTION,
|
||||
{
|
||||
ATTR_ENTITY_ID: f"select.{serial_number}_prebrew_infusion_mode",
|
||||
ATTR_OPTION: "prebrew",
|
||||
},
|
||||
blocking=True,
|
||||
)
|
||||
assert exc_info.value.translation_key == "select_option_error"
|
||||
|
@ -2,6 +2,7 @@
|
||||
|
||||
from unittest.mock import MagicMock
|
||||
|
||||
from lmcloud.exceptions import RequestNotSuccessful
|
||||
import pytest
|
||||
from syrupy import SnapshotAssertion
|
||||
|
||||
@ -12,6 +13,7 @@ from homeassistant.components.switch import (
|
||||
)
|
||||
from homeassistant.const import ATTR_ENTITY_ID
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.exceptions import HomeAssistantError
|
||||
from homeassistant.helpers import device_registry as dr, entity_registry as er
|
||||
|
||||
from . import WAKE_UP_SLEEP_ENTRY_IDS, async_init_integration
|
||||
@ -158,3 +160,56 @@ async def test_auto_on_off_switches(
|
||||
)
|
||||
wake_up_sleep_entry.enabled = True
|
||||
mock_lamarzocco.set_wake_up_sleep.assert_called_with(wake_up_sleep_entry)
|
||||
|
||||
|
||||
async def test_switch_exceptions(
|
||||
hass: HomeAssistant,
|
||||
mock_lamarzocco: MagicMock,
|
||||
mock_config_entry: MockConfigEntry,
|
||||
) -> None:
|
||||
"""Test the La Marzocco switches."""
|
||||
await async_init_integration(hass, mock_config_entry)
|
||||
|
||||
serial_number = mock_lamarzocco.serial_number
|
||||
|
||||
state = hass.states.get(f"switch.{serial_number}")
|
||||
assert state
|
||||
|
||||
mock_lamarzocco.set_power.side_effect = RequestNotSuccessful("Boom")
|
||||
|
||||
with pytest.raises(HomeAssistantError) as exc_info:
|
||||
await hass.services.async_call(
|
||||
SWITCH_DOMAIN,
|
||||
SERVICE_TURN_OFF,
|
||||
{
|
||||
ATTR_ENTITY_ID: f"switch.{serial_number}",
|
||||
},
|
||||
blocking=True,
|
||||
)
|
||||
assert exc_info.value.translation_key == "switch_off_error"
|
||||
|
||||
with pytest.raises(HomeAssistantError) as exc_info:
|
||||
await hass.services.async_call(
|
||||
SWITCH_DOMAIN,
|
||||
SERVICE_TURN_ON,
|
||||
{
|
||||
ATTR_ENTITY_ID: f"switch.{serial_number}",
|
||||
},
|
||||
blocking=True,
|
||||
)
|
||||
assert exc_info.value.translation_key == "switch_on_error"
|
||||
|
||||
state = hass.states.get(f"switch.{serial_number}_auto_on_off_os2oswx")
|
||||
assert state
|
||||
|
||||
mock_lamarzocco.set_wake_up_sleep.side_effect = RequestNotSuccessful("Boom")
|
||||
with pytest.raises(HomeAssistantError) as exc_info:
|
||||
await hass.services.async_call(
|
||||
SWITCH_DOMAIN,
|
||||
SERVICE_TURN_OFF,
|
||||
{
|
||||
ATTR_ENTITY_ID: f"switch.{serial_number}_auto_on_off_os2oswx",
|
||||
},
|
||||
blocking=True,
|
||||
)
|
||||
assert exc_info.value.translation_key == "auto_on_off_error"
|
||||
|
@ -3,6 +3,7 @@
|
||||
from unittest.mock import MagicMock
|
||||
|
||||
from lmcloud.const import FirmwareType
|
||||
from lmcloud.exceptions import RequestNotSuccessful
|
||||
import pytest
|
||||
from syrupy import SnapshotAssertion
|
||||
|
||||
@ -54,17 +55,26 @@ async def test_update_entites(
|
||||
mock_lamarzocco.update_firmware.assert_called_once_with(component)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("attr", "value"),
|
||||
[
|
||||
("side_effect", RequestNotSuccessful("Boom")),
|
||||
("return_value", False),
|
||||
],
|
||||
)
|
||||
async def test_update_error(
|
||||
hass: HomeAssistant,
|
||||
mock_lamarzocco: MagicMock,
|
||||
attr: str,
|
||||
value: bool | Exception,
|
||||
) -> None:
|
||||
"""Test error during update."""
|
||||
state = hass.states.get(f"update.{mock_lamarzocco.serial_number}_machine_firmware")
|
||||
assert state
|
||||
|
||||
mock_lamarzocco.update_firmware.return_value = False
|
||||
setattr(mock_lamarzocco.update_firmware, attr, value)
|
||||
|
||||
with pytest.raises(HomeAssistantError, match="Update failed"):
|
||||
with pytest.raises(HomeAssistantError) as exc_info:
|
||||
await hass.services.async_call(
|
||||
UPDATE_DOMAIN,
|
||||
SERVICE_INSTALL,
|
||||
@ -73,3 +83,4 @@ async def test_update_error(
|
||||
},
|
||||
blocking=True,
|
||||
)
|
||||
assert exc_info.value.translation_key == "update_failed"
|
||||
|
Loading…
x
Reference in New Issue
Block a user