Minor fixes to A. O. Smith integration (#107421)
parent
60fbb8b698
commit
a3352ce457
|
@ -15,6 +15,7 @@ from homeassistant.components.water_heater import (
|
||||||
from homeassistant.config_entries import ConfigEntry
|
from homeassistant.config_entries import ConfigEntry
|
||||||
from homeassistant.const import UnitOfTemperature
|
from homeassistant.const import UnitOfTemperature
|
||||||
from homeassistant.core import HomeAssistant
|
from homeassistant.core import HomeAssistant
|
||||||
|
from homeassistant.exceptions import HomeAssistantError
|
||||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||||
|
|
||||||
from . import AOSmithData
|
from . import AOSmithData
|
||||||
|
@ -35,13 +36,13 @@ MODE_AOSMITH_TO_HA = {
|
||||||
AOSmithOperationMode.VACATION: STATE_OFF,
|
AOSmithOperationMode.VACATION: STATE_OFF,
|
||||||
}
|
}
|
||||||
|
|
||||||
# Operation mode to use when exiting away mode
|
# Priority list for operation mode to use when exiting away mode
|
||||||
DEFAULT_OPERATION_MODE = AOSmithOperationMode.HYBRID
|
# Will use the first mode that is supported by the device
|
||||||
|
DEFAULT_OPERATION_MODE_PRIORITY = [
|
||||||
DEFAULT_SUPPORT_FLAGS = (
|
AOSmithOperationMode.HYBRID,
|
||||||
WaterHeaterEntityFeature.TARGET_TEMPERATURE
|
AOSmithOperationMode.HEAT_PUMP,
|
||||||
| WaterHeaterEntityFeature.OPERATION_MODE
|
AOSmithOperationMode.ELECTRIC,
|
||||||
)
|
]
|
||||||
|
|
||||||
|
|
||||||
async def async_setup_entry(
|
async def async_setup_entry(
|
||||||
|
@ -93,10 +94,16 @@ class AOSmithWaterHeaterEntity(AOSmithStatusEntity, WaterHeaterEntity):
|
||||||
for supported_mode in self.device.supported_modes
|
for supported_mode in self.device.supported_modes
|
||||||
)
|
)
|
||||||
|
|
||||||
if supports_vacation_mode:
|
support_flags = WaterHeaterEntityFeature.TARGET_TEMPERATURE
|
||||||
return DEFAULT_SUPPORT_FLAGS | WaterHeaterEntityFeature.AWAY_MODE
|
|
||||||
|
|
||||||
return DEFAULT_SUPPORT_FLAGS
|
# Operation mode only supported if there is more than one mode
|
||||||
|
if len(self.operation_list) > 1:
|
||||||
|
support_flags |= WaterHeaterEntityFeature.OPERATION_MODE
|
||||||
|
|
||||||
|
if supports_vacation_mode:
|
||||||
|
support_flags |= WaterHeaterEntityFeature.AWAY_MODE
|
||||||
|
|
||||||
|
return support_flags
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def target_temperature(self) -> float | None:
|
def target_temperature(self) -> float | None:
|
||||||
|
@ -120,6 +127,9 @@ class AOSmithWaterHeaterEntity(AOSmithStatusEntity, WaterHeaterEntity):
|
||||||
|
|
||||||
async def async_set_operation_mode(self, operation_mode: str) -> None:
|
async def async_set_operation_mode(self, operation_mode: str) -> None:
|
||||||
"""Set new target operation mode."""
|
"""Set new target operation mode."""
|
||||||
|
if operation_mode not in self.operation_list:
|
||||||
|
raise HomeAssistantError("Operation mode not supported")
|
||||||
|
|
||||||
aosmith_mode = MODE_HA_TO_AOSMITH.get(operation_mode)
|
aosmith_mode = MODE_HA_TO_AOSMITH.get(operation_mode)
|
||||||
if aosmith_mode is not None:
|
if aosmith_mode is not None:
|
||||||
await self.client.update_mode(self.junction_id, aosmith_mode)
|
await self.client.update_mode(self.junction_id, aosmith_mode)
|
||||||
|
@ -142,6 +152,9 @@ class AOSmithWaterHeaterEntity(AOSmithStatusEntity, WaterHeaterEntity):
|
||||||
|
|
||||||
async def async_turn_away_mode_off(self) -> None:
|
async def async_turn_away_mode_off(self) -> None:
|
||||||
"""Turn away mode off."""
|
"""Turn away mode off."""
|
||||||
await self.client.update_mode(self.junction_id, DEFAULT_OPERATION_MODE)
|
supported_aosmith_modes = [x.mode for x in self.device.supported_modes]
|
||||||
|
|
||||||
await self.coordinator.async_request_refresh()
|
for mode in DEFAULT_OPERATION_MODE_PRIORITY:
|
||||||
|
if mode in supported_aosmith_modes:
|
||||||
|
await self.client.update_mode(self.junction_id, mode)
|
||||||
|
break
|
||||||
|
|
|
@ -29,20 +29,10 @@ FIXTURE_USER_INPUT = {
|
||||||
|
|
||||||
|
|
||||||
def build_device_fixture(
|
def build_device_fixture(
|
||||||
mode_pending: bool, setpoint_pending: bool, has_vacation_mode: bool
|
heat_pump: bool, mode_pending: bool, setpoint_pending: bool, has_vacation_mode: bool
|
||||||
):
|
):
|
||||||
"""Build a fixture for a device."""
|
"""Build a fixture for a device."""
|
||||||
supported_modes: list[SupportedOperationModeInfo] = [
|
supported_modes: list[SupportedOperationModeInfo] = [
|
||||||
SupportedOperationModeInfo(
|
|
||||||
mode=OperationMode.HYBRID,
|
|
||||||
original_name="HYBRID",
|
|
||||||
has_day_selection=False,
|
|
||||||
),
|
|
||||||
SupportedOperationModeInfo(
|
|
||||||
mode=OperationMode.HEAT_PUMP,
|
|
||||||
original_name="HEAT_PUMP",
|
|
||||||
has_day_selection=False,
|
|
||||||
),
|
|
||||||
SupportedOperationModeInfo(
|
SupportedOperationModeInfo(
|
||||||
mode=OperationMode.ELECTRIC,
|
mode=OperationMode.ELECTRIC,
|
||||||
original_name="ELECTRIC",
|
original_name="ELECTRIC",
|
||||||
|
@ -50,6 +40,22 @@ def build_device_fixture(
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
|
|
||||||
|
if heat_pump:
|
||||||
|
supported_modes.append(
|
||||||
|
SupportedOperationModeInfo(
|
||||||
|
mode=OperationMode.HYBRID,
|
||||||
|
original_name="HYBRID",
|
||||||
|
has_day_selection=False,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
supported_modes.append(
|
||||||
|
SupportedOperationModeInfo(
|
||||||
|
mode=OperationMode.HEAT_PUMP,
|
||||||
|
original_name="HEAT_PUMP",
|
||||||
|
has_day_selection=False,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
if has_vacation_mode:
|
if has_vacation_mode:
|
||||||
supported_modes.append(
|
supported_modes.append(
|
||||||
SupportedOperationModeInfo(
|
SupportedOperationModeInfo(
|
||||||
|
@ -59,10 +65,18 @@ def build_device_fixture(
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
device_type = (
|
||||||
|
DeviceType.NEXT_GEN_HEAT_PUMP if heat_pump else DeviceType.RE3_CONNECTED
|
||||||
|
)
|
||||||
|
|
||||||
|
current_mode = OperationMode.HEAT_PUMP if heat_pump else OperationMode.ELECTRIC
|
||||||
|
|
||||||
|
model = "HPTS-50 200 202172000" if heat_pump else "EE12-50H55DVF 100,3806368"
|
||||||
|
|
||||||
return Device(
|
return Device(
|
||||||
brand="aosmith",
|
brand="aosmith",
|
||||||
model="HPTS-50 200 202172000",
|
model=model,
|
||||||
device_type=DeviceType.NEXT_GEN_HEAT_PUMP,
|
device_type=device_type,
|
||||||
dsn="dsn",
|
dsn="dsn",
|
||||||
junction_id="junctionId",
|
junction_id="junctionId",
|
||||||
name="My water heater",
|
name="My water heater",
|
||||||
|
@ -72,7 +86,7 @@ def build_device_fixture(
|
||||||
status=DeviceStatus(
|
status=DeviceStatus(
|
||||||
firmware_version="2.14",
|
firmware_version="2.14",
|
||||||
is_online=True,
|
is_online=True,
|
||||||
current_mode=OperationMode.HEAT_PUMP,
|
current_mode=current_mode,
|
||||||
mode_change_pending=mode_pending,
|
mode_change_pending=mode_pending,
|
||||||
temperature_setpoint=130,
|
temperature_setpoint=130,
|
||||||
temperature_setpoint_pending=setpoint_pending,
|
temperature_setpoint_pending=setpoint_pending,
|
||||||
|
@ -121,6 +135,12 @@ def mock_setup_entry() -> Generator[AsyncMock, None, None]:
|
||||||
yield mock_setup_entry
|
yield mock_setup_entry
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def get_devices_fixture_heat_pump() -> bool:
|
||||||
|
"""Return whether the device in the get_devices fixture should be a heat pump water heater."""
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def get_devices_fixture_mode_pending() -> bool:
|
def get_devices_fixture_mode_pending() -> bool:
|
||||||
"""Return whether to set mode_pending in the get_devices fixture."""
|
"""Return whether to set mode_pending in the get_devices fixture."""
|
||||||
|
@ -141,6 +161,7 @@ def get_devices_fixture_has_vacation_mode() -> bool:
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
async def mock_client(
|
async def mock_client(
|
||||||
|
get_devices_fixture_heat_pump: bool,
|
||||||
get_devices_fixture_mode_pending: bool,
|
get_devices_fixture_mode_pending: bool,
|
||||||
get_devices_fixture_setpoint_pending: bool,
|
get_devices_fixture_setpoint_pending: bool,
|
||||||
get_devices_fixture_has_vacation_mode: bool,
|
get_devices_fixture_has_vacation_mode: bool,
|
||||||
|
@ -148,9 +169,10 @@ async def mock_client(
|
||||||
"""Return a mocked client."""
|
"""Return a mocked client."""
|
||||||
get_devices_fixture = [
|
get_devices_fixture = [
|
||||||
build_device_fixture(
|
build_device_fixture(
|
||||||
get_devices_fixture_mode_pending,
|
heat_pump=get_devices_fixture_heat_pump,
|
||||||
get_devices_fixture_setpoint_pending,
|
mode_pending=get_devices_fixture_mode_pending,
|
||||||
get_devices_fixture_has_vacation_mode,
|
setpoint_pending=get_devices_fixture_setpoint_pending,
|
||||||
|
has_vacation_mode=get_devices_fixture_has_vacation_mode,
|
||||||
)
|
)
|
||||||
]
|
]
|
||||||
get_all_device_info_fixture = load_json_object_fixture(
|
get_all_device_info_fixture = load_json_object_fixture(
|
||||||
|
|
|
@ -8,9 +8,9 @@
|
||||||
'max_temp': 130,
|
'max_temp': 130,
|
||||||
'min_temp': 95,
|
'min_temp': 95,
|
||||||
'operation_list': list([
|
'operation_list': list([
|
||||||
|
'electric',
|
||||||
'eco',
|
'eco',
|
||||||
'heat_pump',
|
'heat_pump',
|
||||||
'electric',
|
|
||||||
]),
|
]),
|
||||||
'operation_mode': 'heat_pump',
|
'operation_mode': 'heat_pump',
|
||||||
'supported_features': <WaterHeaterEntityFeature: 7>,
|
'supported_features': <WaterHeaterEntityFeature: 7>,
|
||||||
|
@ -25,3 +25,23 @@
|
||||||
'state': 'heat_pump',
|
'state': 'heat_pump',
|
||||||
})
|
})
|
||||||
# ---
|
# ---
|
||||||
|
# name: test_state_non_heat_pump[False]
|
||||||
|
StateSnapshot({
|
||||||
|
'attributes': ReadOnlyDict({
|
||||||
|
'away_mode': 'off',
|
||||||
|
'current_temperature': None,
|
||||||
|
'friendly_name': 'My water heater',
|
||||||
|
'max_temp': 130,
|
||||||
|
'min_temp': 95,
|
||||||
|
'supported_features': <WaterHeaterEntityFeature: 5>,
|
||||||
|
'target_temp_high': None,
|
||||||
|
'target_temp_low': None,
|
||||||
|
'temperature': 130,
|
||||||
|
}),
|
||||||
|
'context': <ANY>,
|
||||||
|
'entity_id': 'water_heater.my_water_heater',
|
||||||
|
'last_changed': <ANY>,
|
||||||
|
'last_updated': <ANY>,
|
||||||
|
'state': 'electric',
|
||||||
|
})
|
||||||
|
# ---
|
||||||
|
|
|
@ -50,7 +50,14 @@ async def test_config_entry_not_ready_get_energy_use_data_error(
|
||||||
"""Test the config entry not ready when get_energy_use_data fails."""
|
"""Test the config entry not ready when get_energy_use_data fails."""
|
||||||
mock_config_entry.add_to_hass(hass)
|
mock_config_entry.add_to_hass(hass)
|
||||||
|
|
||||||
get_devices_fixture = [build_device_fixture(False, False, True)]
|
get_devices_fixture = [
|
||||||
|
build_device_fixture(
|
||||||
|
heat_pump=True,
|
||||||
|
mode_pending=False,
|
||||||
|
setpoint_pending=False,
|
||||||
|
has_vacation_mode=True,
|
||||||
|
)
|
||||||
|
]
|
||||||
|
|
||||||
with patch(
|
with patch(
|
||||||
"homeassistant.components.aosmith.config_flow.AOSmithAPIClient.get_devices",
|
"homeassistant.components.aosmith.config_flow.AOSmithAPIClient.get_devices",
|
||||||
|
|
|
@ -25,6 +25,7 @@ from homeassistant.const import (
|
||||||
ATTR_SUPPORTED_FEATURES,
|
ATTR_SUPPORTED_FEATURES,
|
||||||
)
|
)
|
||||||
from homeassistant.core import HomeAssistant
|
from homeassistant.core import HomeAssistant
|
||||||
|
from homeassistant.exceptions import HomeAssistantError
|
||||||
from homeassistant.helpers import entity_registry as er
|
from homeassistant.helpers import entity_registry as er
|
||||||
|
|
||||||
from tests.common import MockConfigEntry
|
from tests.common import MockConfigEntry
|
||||||
|
@ -53,6 +54,20 @@ async def test_state(
|
||||||
assert state == snapshot
|
assert state == snapshot
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
("get_devices_fixture_heat_pump"),
|
||||||
|
[
|
||||||
|
False,
|
||||||
|
],
|
||||||
|
)
|
||||||
|
async def test_state_non_heat_pump(
|
||||||
|
hass: HomeAssistant, init_integration: MockConfigEntry, snapshot: SnapshotAssertion
|
||||||
|
) -> None:
|
||||||
|
"""Test the state of the water heater entity for a non heat pump device."""
|
||||||
|
state = hass.states.get("water_heater.my_water_heater")
|
||||||
|
assert state == snapshot
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
("get_devices_fixture_has_vacation_mode"),
|
("get_devices_fixture_has_vacation_mode"),
|
||||||
[False],
|
[False],
|
||||||
|
@ -98,6 +113,24 @@ async def test_set_operation_mode(
|
||||||
mock_client.update_mode.assert_called_once_with("junctionId", aosmith_mode)
|
mock_client.update_mode.assert_called_once_with("junctionId", aosmith_mode)
|
||||||
|
|
||||||
|
|
||||||
|
async def test_unsupported_operation_mode(
|
||||||
|
hass: HomeAssistant,
|
||||||
|
mock_client: MagicMock,
|
||||||
|
init_integration: MockConfigEntry,
|
||||||
|
) -> None:
|
||||||
|
"""Test setting the operation mode with an unsupported mode."""
|
||||||
|
with pytest.raises(HomeAssistantError):
|
||||||
|
await hass.services.async_call(
|
||||||
|
WATER_HEATER_DOMAIN,
|
||||||
|
SERVICE_SET_OPERATION_MODE,
|
||||||
|
{
|
||||||
|
ATTR_ENTITY_ID: "water_heater.my_water_heater",
|
||||||
|
ATTR_OPERATION_MODE: "unsupported_mode",
|
||||||
|
},
|
||||||
|
blocking=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
async def test_set_temperature(
|
async def test_set_temperature(
|
||||||
hass: HomeAssistant,
|
hass: HomeAssistant,
|
||||||
mock_client: MagicMock,
|
mock_client: MagicMock,
|
||||||
|
@ -115,10 +148,12 @@ async def test_set_temperature(
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
("hass_away_mode", "aosmith_mode"),
|
("get_devices_fixture_heat_pump", "hass_away_mode", "aosmith_mode"),
|
||||||
[
|
[
|
||||||
(True, OperationMode.VACATION),
|
(True, True, OperationMode.VACATION),
|
||||||
(False, OperationMode.HYBRID),
|
(True, False, OperationMode.HYBRID),
|
||||||
|
(False, True, OperationMode.VACATION),
|
||||||
|
(False, False, OperationMode.ELECTRIC),
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
async def test_away_mode(
|
async def test_away_mode(
|
||||||
|
|
Loading…
Reference in New Issue