Handle MELCloud Home climate action errors (#183646)

This commit is contained in:
Erwin Douna
2026-09-30 11:26:18 +02:00
committed by GitHub
parent ef2bc757e6
commit 527ed24bc1
2 changed files with 193 additions and 43 deletions
@@ -20,7 +20,7 @@ from homeassistant.const import ATTR_TEMPERATURE, UnitOfTemperature
from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
from .common import async_setup_unit_entities
from .common import async_setup_unit_entities, perform_action
from .coordinator import MelCloudHomeConfigEntry, MelCloudHomeCoordinator
from .entity import MelCloudHomeATAUnitEntity, MelCloudHomeATWZoneEntity
@@ -253,22 +253,29 @@ class ATAClimateEntity(MelCloudHomeATAUnitEntity, ClimateEntity):
async def async_set_hvac_mode(self, hvac_mode: HVACMode) -> None:
"""Set the HVAC mode."""
if hvac_mode == HVACMode.OFF:
await self.coordinator.client.control_ata_unit(self._unit_id, power=False)
await perform_action(
self.coordinator,
self.coordinator.client.control_ata_unit(self._unit_id, power=False),
)
else:
await self.coordinator.client.control_ata_unit(
self._unit_id,
power=True,
operation_mode=ATA_HVAC_MODE_TO_OPERATION[hvac_mode],
await perform_action(
self.coordinator,
self.coordinator.client.control_ata_unit(
self._unit_id,
power=True,
operation_mode=ATA_HVAC_MODE_TO_OPERATION[hvac_mode],
),
)
await self.coordinator.async_request_refresh()
@override
async def async_set_temperature(self, **kwargs: Any) -> None:
"""Set the target temperature."""
await self.coordinator.client.control_ata_unit(
self._unit_id, set_temperature=kwargs[ATTR_TEMPERATURE]
await perform_action(
self.coordinator,
self.coordinator.client.control_ata_unit(
self._unit_id, set_temperature=kwargs[ATTR_TEMPERATURE]
),
)
await self.coordinator.async_request_refresh()
@property
@override
@@ -285,39 +292,52 @@ class ATAClimateEntity(MelCloudHomeATAUnitEntity, ClimateEntity):
@override
async def async_set_swing_horizontal_mode(self, swing_horizontal_mode: str) -> None:
"""Set the horizontal vane direction."""
await self.coordinator.client.control_ata_unit(
self._unit_id,
vane_horizontal_direction=HA_VANE_HORIZONTAL_TO_ATA[swing_horizontal_mode],
await perform_action(
self.coordinator,
self.coordinator.client.control_ata_unit(
self._unit_id,
vane_horizontal_direction=HA_VANE_HORIZONTAL_TO_ATA[
swing_horizontal_mode
],
),
)
await self.coordinator.async_request_refresh()
@override
async def async_set_swing_mode(self, swing_mode: str) -> None:
"""Set the vertical vane direction."""
await self.coordinator.client.control_ata_unit(
self._unit_id, vane_vertical_direction=HA_VANE_VERTICAL_TO_ATA[swing_mode]
await perform_action(
self.coordinator,
self.coordinator.client.control_ata_unit(
self._unit_id,
vane_vertical_direction=HA_VANE_VERTICAL_TO_ATA[swing_mode],
),
)
await self.coordinator.async_request_refresh()
@override
async def async_set_fan_mode(self, fan_mode: str) -> None:
"""Set the fan mode."""
await self.coordinator.client.control_ata_unit(
self._unit_id, set_fan_speed=HA_FAN_SPEED_TO_ATA[fan_mode]
await perform_action(
self.coordinator,
self.coordinator.client.control_ata_unit(
self._unit_id, set_fan_speed=HA_FAN_SPEED_TO_ATA[fan_mode]
),
)
await self.coordinator.async_request_refresh()
@override
async def async_turn_on(self) -> None:
"""Turn the unit on."""
await self.coordinator.client.control_ata_unit(self._unit_id, power=True)
await self.coordinator.async_request_refresh()
await perform_action(
self.coordinator,
self.coordinator.client.control_ata_unit(self._unit_id, power=True),
)
@override
async def async_turn_off(self) -> None:
"""Turn the unit off."""
await self.coordinator.client.control_ata_unit(self._unit_id, power=False)
await self.coordinator.async_request_refresh()
await perform_action(
self.coordinator,
self.coordinator.client.control_ata_unit(self._unit_id, power=False),
)
class ATWZoneClimateEntity(MelCloudHomeATWZoneEntity, ClimateEntity):
@@ -414,45 +434,62 @@ class ATWZoneClimateEntity(MelCloudHomeATWZoneEntity, ClimateEntity):
async def async_set_hvac_mode(self, hvac_mode: HVACMode) -> None:
"""Set the HVAC mode."""
if hvac_mode == HVACMode.OFF:
await self.coordinator.client.control_atw_unit(self._unit_id, power=False)
await perform_action(
self.coordinator,
self.coordinator.client.control_atw_unit(self._unit_id, power=False),
)
else:
zone_mode = HVAC_MODE_TO_ATW_ZONE_MODE[hvac_mode]
if self.zone_number == 1:
await self.coordinator.client.control_atw_unit(
self._unit_id,
power=True,
operation_mode_zone1=zone_mode,
await perform_action(
self.coordinator,
self.coordinator.client.control_atw_unit(
self._unit_id,
power=True,
operation_mode_zone1=zone_mode,
),
)
else:
await self.coordinator.client.control_atw_unit(
self._unit_id,
power=True,
operation_mode_zone2=zone_mode,
await perform_action(
self.coordinator,
self.coordinator.client.control_atw_unit(
self._unit_id,
power=True,
operation_mode_zone2=zone_mode,
),
)
await self.coordinator.async_request_refresh()
@override
async def async_set_temperature(self, **kwargs: Any) -> None:
"""Set the target temperature."""
temperature = kwargs[ATTR_TEMPERATURE]
if self.zone_number == 1:
await self.coordinator.client.control_atw_unit(
self._unit_id, set_temperature_zone1=temperature
await perform_action(
self.coordinator,
self.coordinator.client.control_atw_unit(
self._unit_id, set_temperature_zone1=temperature
),
)
else:
await self.coordinator.client.control_atw_unit(
self._unit_id, set_temperature_zone2=temperature
await perform_action(
self.coordinator,
self.coordinator.client.control_atw_unit(
self._unit_id, set_temperature_zone2=temperature
),
)
await self.coordinator.async_request_refresh()
@override
async def async_turn_on(self) -> None:
"""Turn the zone on."""
await self.coordinator.client.control_atw_unit(self._unit_id, power=True)
await self.coordinator.async_request_refresh()
await perform_action(
self.coordinator,
self.coordinator.client.control_atw_unit(self._unit_id, power=True),
)
@override
async def async_turn_off(self) -> None:
"""Turn the zone off."""
await self.coordinator.client.control_atw_unit(self._unit_id, power=False)
await self.coordinator.async_request_refresh()
await perform_action(
self.coordinator,
self.coordinator.client.control_atw_unit(self._unit_id, power=False),
)
@@ -10,6 +10,9 @@ from aiomelcloudhome import (
ATAVaneHorizontal,
ATAVaneVertical,
ATWZoneMode,
MelCloudHomeAuthenticationError,
MelCloudHomeConnectionError,
MelCloudHomeTimeoutError,
UserContext,
)
import pytest
@@ -36,6 +39,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
@@ -521,3 +525,112 @@ async def test_atw_zone_temperature_range(
assert (state2 := hass.states.get(ATW_ZONE2_ENTITY_ID))
assert state2.attributes["min_temp"] == 12.0
assert state2.attributes["max_temp"] == 28.0
@pytest.mark.parametrize(
("entity_id", "method", "service", "service_data"),
[
pytest.param(
ATA_ENTITY_ID,
"control_ata_unit",
SERVICE_SET_HVAC_MODE,
{ATTR_HVAC_MODE: HVACMode.HEAT},
id="ata_set_hvac_mode",
),
pytest.param(
ATA_ENTITY_ID,
"control_ata_unit",
SERVICE_SET_TEMPERATURE,
{ATTR_TEMPERATURE: 23.0},
id="ata_set_temperature",
),
pytest.param(
ATA_ENTITY_ID,
"control_ata_unit",
SERVICE_SET_FAN_MODE,
{ATTR_FAN_MODE: "auto"},
id="ata_set_fan_mode",
),
pytest.param(
ATA_ENTITY_ID,
"control_ata_unit",
SERVICE_SET_SWING_MODE,
{ATTR_SWING_MODE: "auto"},
id="ata_set_swing_mode",
),
pytest.param(
ATA_ENTITY_ID,
"control_ata_unit",
SERVICE_SET_SWING_HORIZONTAL_MODE,
{ATTR_SWING_HORIZONTAL_MODE: "auto"},
id="ata_set_swing_horizontal_mode",
),
pytest.param(
ATA_ENTITY_ID, "control_ata_unit", SERVICE_TURN_ON, {}, id="ata_turn_on"
),
pytest.param(
ATA_ENTITY_ID, "control_ata_unit", SERVICE_TURN_OFF, {}, id="ata_turn_off"
),
pytest.param(
ATW_ZONE1_ENTITY_ID,
"control_atw_unit",
SERVICE_SET_HVAC_MODE,
{ATTR_HVAC_MODE: HVACMode.HEAT},
id="atw_set_hvac_mode",
),
pytest.param(
ATW_ZONE1_ENTITY_ID,
"control_atw_unit",
SERVICE_SET_TEMPERATURE,
{ATTR_TEMPERATURE: 23.0},
id="atw_set_temperature",
),
pytest.param(
ATW_ZONE1_ENTITY_ID,
"control_atw_unit",
SERVICE_TURN_ON,
{},
id="atw_turn_on",
),
pytest.param(
ATW_ZONE1_ENTITY_ID,
"control_atw_unit",
SERVICE_TURN_OFF,
{},
id="atw_turn_off",
),
],
)
@pytest.mark.parametrize(
("raise_exception", "translation_key"),
[
(MelCloudHomeAuthenticationError, "invalid_auth"),
(MelCloudHomeConnectionError, "cannot_connect"),
(MelCloudHomeTimeoutError, "timeout_connect"),
],
)
async def test_action_exceptions(
hass: HomeAssistant,
mock_config_entry: MockConfigEntry,
mock_melcloud_client: AsyncMock,
entity_id: str,
method: str,
service: str,
service_data: dict[str, Any],
raise_exception: type[Exception],
translation_key: str,
) -> None:
"""Test climate actions raise translated errors on client errors."""
await setup_integration(hass, mock_config_entry)
getattr(mock_melcloud_client, method).side_effect = raise_exception
with pytest.raises(HomeAssistantError) as exc_info:
await hass.services.async_call(
CLIMATE_DOMAIN,
service,
{ATTR_ENTITY_ID: entity_id, **service_data},
blocking=True,
)
assert exc_info.value.translation_key == translation_key