Add Hot Water+ Level select entity to A. O. Smith integration (#151548)
parent
c91d64e04d
commit
ec6a052ff5
|
@ -16,7 +16,7 @@ from .coordinator import (
|
|||
AOSmithStatusCoordinator,
|
||||
)
|
||||
|
||||
PLATFORMS: list[Platform] = [Platform.SENSOR, Platform.WATER_HEATER]
|
||||
PLATFORMS: list[Platform] = [Platform.SELECT, Platform.SENSOR, Platform.WATER_HEATER]
|
||||
|
||||
|
||||
async def async_setup_entry(hass: HomeAssistant, entry: AOSmithConfigEntry) -> bool:
|
||||
|
|
|
@ -1,5 +1,10 @@
|
|||
{
|
||||
"entity": {
|
||||
"select": {
|
||||
"hot_water_plus_level": {
|
||||
"default": "mdi:water-plus"
|
||||
}
|
||||
},
|
||||
"sensor": {
|
||||
"hot_water_availability": {
|
||||
"default": "mdi:water-thermometer"
|
||||
|
|
|
@ -0,0 +1,70 @@
|
|||
"""The select platform for the A. O. Smith integration."""
|
||||
|
||||
from homeassistant.components.select import SelectEntity
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
|
||||
|
||||
from . import AOSmithConfigEntry
|
||||
from .coordinator import AOSmithStatusCoordinator
|
||||
from .entity import AOSmithStatusEntity
|
||||
|
||||
HWP_LEVEL_HA_TO_AOSMITH = {
|
||||
"off": 0,
|
||||
"level1": 1,
|
||||
"level2": 2,
|
||||
"level3": 3,
|
||||
}
|
||||
HWP_LEVEL_AOSMITH_TO_HA = {value: key for key, value in HWP_LEVEL_HA_TO_AOSMITH.items()}
|
||||
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
entry: AOSmithConfigEntry,
|
||||
async_add_entities: AddConfigEntryEntitiesCallback,
|
||||
) -> None:
|
||||
"""Set up A. O. Smith select platform."""
|
||||
data = entry.runtime_data
|
||||
|
||||
async_add_entities(
|
||||
AOSmithHotWaterPlusSelectEntity(data.status_coordinator, device.junction_id)
|
||||
for device in data.status_coordinator.data.values()
|
||||
if device.supports_hot_water_plus
|
||||
)
|
||||
|
||||
|
||||
class AOSmithHotWaterPlusSelectEntity(AOSmithStatusEntity, SelectEntity):
|
||||
"""Class for the Hot Water+ select entity."""
|
||||
|
||||
_attr_translation_key = "hot_water_plus_level"
|
||||
_attr_options = list(HWP_LEVEL_HA_TO_AOSMITH)
|
||||
|
||||
def __init__(self, coordinator: AOSmithStatusCoordinator, junction_id: str) -> None:
|
||||
"""Initialize the entity."""
|
||||
super().__init__(coordinator, junction_id)
|
||||
self._attr_unique_id = f"hot_water_plus_level_{junction_id}"
|
||||
|
||||
@property
|
||||
def suggested_object_id(self) -> str | None:
|
||||
"""Override the suggested object id to make '+' get converted to 'plus' in the entity id."""
|
||||
return "hot_water_plus_level"
|
||||
|
||||
@property
|
||||
def current_option(self) -> str | None:
|
||||
"""Return the current Hot Water+ mode."""
|
||||
hot_water_plus_level = self.device.status.hot_water_plus_level
|
||||
return (
|
||||
None
|
||||
if hot_water_plus_level is None
|
||||
else HWP_LEVEL_AOSMITH_TO_HA.get(hot_water_plus_level)
|
||||
)
|
||||
|
||||
async def async_select_option(self, option: str) -> None:
|
||||
"""Set the Hot Water+ mode."""
|
||||
aosmith_hwp_level = HWP_LEVEL_HA_TO_AOSMITH[option]
|
||||
await self.client.update_mode(
|
||||
junction_id=self.junction_id,
|
||||
mode=self.device.status.current_mode,
|
||||
hot_water_plus_level=aosmith_hwp_level,
|
||||
)
|
||||
|
||||
await self.coordinator.async_request_refresh()
|
|
@ -26,6 +26,17 @@
|
|||
}
|
||||
},
|
||||
"entity": {
|
||||
"select": {
|
||||
"hot_water_plus_level": {
|
||||
"name": "Hot Water+ level",
|
||||
"state": {
|
||||
"off": "[%key:common::state::off%]",
|
||||
"level1": "Level 1",
|
||||
"level2": "Level 2",
|
||||
"level3": "Level 3"
|
||||
}
|
||||
}
|
||||
},
|
||||
"sensor": {
|
||||
"hot_water_availability": {
|
||||
"name": "Hot water availability"
|
||||
|
|
|
@ -29,7 +29,11 @@ FIXTURE_USER_INPUT = {
|
|||
|
||||
|
||||
def build_device_fixture(
|
||||
heat_pump: bool, mode_pending: bool, setpoint_pending: bool, has_vacation_mode: bool
|
||||
heat_pump: bool,
|
||||
mode_pending: bool,
|
||||
setpoint_pending: bool,
|
||||
has_vacation_mode: bool,
|
||||
supports_hot_water_plus: bool,
|
||||
):
|
||||
"""Build a fixture for a device."""
|
||||
supported_modes: list[SupportedOperationModeInfo] = [
|
||||
|
@ -37,7 +41,7 @@ def build_device_fixture(
|
|||
mode=OperationMode.ELECTRIC,
|
||||
original_name="ELECTRIC",
|
||||
has_day_selection=True,
|
||||
supports_hot_water_plus=False,
|
||||
supports_hot_water_plus=supports_hot_water_plus,
|
||||
),
|
||||
]
|
||||
|
||||
|
@ -47,7 +51,7 @@ def build_device_fixture(
|
|||
mode=OperationMode.HYBRID,
|
||||
original_name="HYBRID",
|
||||
has_day_selection=False,
|
||||
supports_hot_water_plus=False,
|
||||
supports_hot_water_plus=supports_hot_water_plus,
|
||||
)
|
||||
)
|
||||
supported_modes.append(
|
||||
|
@ -55,7 +59,7 @@ def build_device_fixture(
|
|||
mode=OperationMode.HEAT_PUMP,
|
||||
original_name="HEAT_PUMP",
|
||||
has_day_selection=False,
|
||||
supports_hot_water_plus=False,
|
||||
supports_hot_water_plus=supports_hot_water_plus,
|
||||
)
|
||||
)
|
||||
|
||||
|
@ -69,17 +73,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"
|
||||
if heat_pump and supports_hot_water_plus:
|
||||
device_type = DeviceType.RE3_PREMIUM
|
||||
elif heat_pump:
|
||||
device_type = DeviceType.NEXT_GEN_HEAT_PUMP
|
||||
else:
|
||||
device_type = DeviceType.RE3_CONNECTED
|
||||
|
||||
return Device(
|
||||
brand="aosmith",
|
||||
model=model,
|
||||
model="Example model",
|
||||
device_type=device_type,
|
||||
dsn="dsn",
|
||||
junction_id="junctionId",
|
||||
|
@ -87,7 +92,7 @@ def build_device_fixture(
|
|||
serial="serial",
|
||||
install_location="Basement",
|
||||
supported_modes=supported_modes,
|
||||
supports_hot_water_plus=False,
|
||||
supports_hot_water_plus=supports_hot_water_plus,
|
||||
status=DeviceStatus(
|
||||
firmware_version="2.14",
|
||||
is_online=True,
|
||||
|
@ -98,7 +103,7 @@ def build_device_fixture(
|
|||
temperature_setpoint_previous=130,
|
||||
temperature_setpoint_maximum=130,
|
||||
hot_water_status=90,
|
||||
hot_water_plus_level=None,
|
||||
hot_water_plus_level=1 if supports_hot_water_plus else None,
|
||||
),
|
||||
)
|
||||
|
||||
|
@ -165,6 +170,12 @@ def get_devices_fixture_has_vacation_mode() -> bool:
|
|||
return True
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def get_devices_fixture_supports_hot_water_plus() -> bool:
|
||||
"""Return whether to include hot water plus support in the get_devices fixture."""
|
||||
return False
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
async def mock_client(
|
||||
hass: HomeAssistant,
|
||||
|
@ -172,6 +183,7 @@ async def mock_client(
|
|||
get_devices_fixture_mode_pending: bool,
|
||||
get_devices_fixture_setpoint_pending: bool,
|
||||
get_devices_fixture_has_vacation_mode: bool,
|
||||
get_devices_fixture_supports_hot_water_plus: bool,
|
||||
) -> Generator[MagicMock]:
|
||||
"""Return a mocked client."""
|
||||
get_devices_fixture = [
|
||||
|
@ -180,6 +192,7 @@ async def mock_client(
|
|||
mode_pending=get_devices_fixture_mode_pending,
|
||||
setpoint_pending=get_devices_fixture_setpoint_pending,
|
||||
has_vacation_mode=get_devices_fixture_has_vacation_mode,
|
||||
supports_hot_water_plus=get_devices_fixture_supports_hot_water_plus,
|
||||
)
|
||||
]
|
||||
get_all_device_info_fixture = await async_load_json_object_fixture(
|
||||
|
|
|
@ -20,7 +20,7 @@
|
|||
'labels': set({
|
||||
}),
|
||||
'manufacturer': 'A. O. Smith',
|
||||
'model': 'HPTS-50 200 202172000',
|
||||
'model': 'Example model',
|
||||
'model_id': None,
|
||||
'name': 'My water heater',
|
||||
'name_by_user': None,
|
||||
|
|
|
@ -0,0 +1,62 @@
|
|||
# serializer version: 1
|
||||
# name: test_state[True][select.my_water_heater_hot_water_plus_level-entry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': set({
|
||||
}),
|
||||
'area_id': None,
|
||||
'capabilities': dict({
|
||||
'options': list([
|
||||
'off',
|
||||
'level1',
|
||||
'level2',
|
||||
'level3',
|
||||
]),
|
||||
}),
|
||||
'config_entry_id': <ANY>,
|
||||
'config_subentry_id': <ANY>,
|
||||
'device_class': None,
|
||||
'device_id': <ANY>,
|
||||
'disabled_by': None,
|
||||
'domain': 'select',
|
||||
'entity_category': None,
|
||||
'entity_id': 'select.my_water_heater_hot_water_plus_level',
|
||||
'has_entity_name': True,
|
||||
'hidden_by': None,
|
||||
'icon': None,
|
||||
'id': <ANY>,
|
||||
'labels': set({
|
||||
}),
|
||||
'name': None,
|
||||
'options': dict({
|
||||
}),
|
||||
'original_device_class': None,
|
||||
'original_icon': None,
|
||||
'original_name': 'Hot Water+ level',
|
||||
'platform': 'aosmith',
|
||||
'previous_unique_id': None,
|
||||
'suggested_object_id': None,
|
||||
'supported_features': 0,
|
||||
'translation_key': 'hot_water_plus_level',
|
||||
'unique_id': 'hot_water_plus_level_junctionId',
|
||||
'unit_of_measurement': None,
|
||||
})
|
||||
# ---
|
||||
# name: test_state[True][select.my_water_heater_hot_water_plus_level-state]
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
'friendly_name': 'My water heater Hot Water+ level',
|
||||
'options': list([
|
||||
'off',
|
||||
'level1',
|
||||
'level2',
|
||||
'level3',
|
||||
]),
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'select.my_water_heater_hot_water_plus_level',
|
||||
'last_changed': <ANY>,
|
||||
'last_reported': <ANY>,
|
||||
'last_updated': <ANY>,
|
||||
'state': 'level1',
|
||||
})
|
||||
# ---
|
|
@ -56,6 +56,7 @@ async def test_config_entry_not_ready_get_energy_use_data_error(
|
|||
mode_pending=False,
|
||||
setpoint_pending=False,
|
||||
has_vacation_mode=True,
|
||||
supports_hot_water_plus=False,
|
||||
)
|
||||
]
|
||||
|
||||
|
|
|
@ -0,0 +1,77 @@
|
|||
"""Tests for the select platform of the A. O. Smith integration."""
|
||||
|
||||
from collections.abc import AsyncGenerator
|
||||
from unittest.mock import MagicMock, patch
|
||||
|
||||
from py_aosmith.models import OperationMode
|
||||
import pytest
|
||||
from syrupy.assertion import SnapshotAssertion
|
||||
|
||||
from homeassistant.components.select import (
|
||||
DOMAIN as SELECT_DOMAIN,
|
||||
SERVICE_SELECT_OPTION,
|
||||
)
|
||||
from homeassistant.const import ATTR_ENTITY_ID, ATTR_OPTION, Platform
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers import entity_registry as er
|
||||
|
||||
from tests.common import MockConfigEntry, snapshot_platform
|
||||
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
async def platforms() -> AsyncGenerator[None]:
|
||||
"""Return the platforms to be loaded for this test."""
|
||||
with patch("homeassistant.components.aosmith.PLATFORMS", [Platform.SELECT]):
|
||||
yield
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("get_devices_fixture_supports_hot_water_plus"),
|
||||
[True],
|
||||
)
|
||||
async def test_state(
|
||||
hass: HomeAssistant,
|
||||
init_integration: MockConfigEntry,
|
||||
snapshot: SnapshotAssertion,
|
||||
entity_registry: er.EntityRegistry,
|
||||
) -> None:
|
||||
"""Test the state of the select entity."""
|
||||
await snapshot_platform(hass, entity_registry, snapshot, init_integration.entry_id)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("get_devices_fixture_supports_hot_water_plus"),
|
||||
[True],
|
||||
)
|
||||
@pytest.mark.parametrize(
|
||||
("hass_level", "aosmith_level"),
|
||||
[
|
||||
("off", 0),
|
||||
("level1", 1),
|
||||
("level2", 2),
|
||||
("level3", 3),
|
||||
],
|
||||
)
|
||||
async def test_set_hot_water_plus_level(
|
||||
hass: HomeAssistant,
|
||||
mock_client: MagicMock,
|
||||
init_integration: MockConfigEntry,
|
||||
hass_level: str,
|
||||
aosmith_level: int,
|
||||
) -> None:
|
||||
"""Test setting the Hot Water+ level."""
|
||||
await hass.services.async_call(
|
||||
SELECT_DOMAIN,
|
||||
SERVICE_SELECT_OPTION,
|
||||
{
|
||||
ATTR_ENTITY_ID: "select.my_water_heater_hot_water_plus_level",
|
||||
ATTR_OPTION: hass_level,
|
||||
},
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
mock_client.update_mode.assert_called_once_with(
|
||||
junction_id="junctionId",
|
||||
mode=OperationMode.HEAT_PUMP,
|
||||
hot_water_plus_level=aosmith_level,
|
||||
)
|
Loading…
Reference in New Issue