Update vallox_websocket_api to 5.0.2 (#110752)

Co-authored-by: Sebastian Lövdahl <slovdahl@hibox.fi>
This commit is contained in:
Jevgeni Kiski 2024-02-19 15:36:51 +02:00 committed by GitHub
parent efac3b0a60
commit 2250baab21
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
20 changed files with 349 additions and 445 deletions

View File

@ -1456,8 +1456,8 @@ build.json @home-assistant/supervisor
/tests/components/v2c/ @dgomes /tests/components/v2c/ @dgomes
/homeassistant/components/vacuum/ @home-assistant/core /homeassistant/components/vacuum/ @home-assistant/core
/tests/components/vacuum/ @home-assistant/core /tests/components/vacuum/ @home-assistant/core
/homeassistant/components/vallox/ @andre-richter @slovdahl @viiru- /homeassistant/components/vallox/ @andre-richter @slovdahl @viiru- @yozik04
/tests/components/vallox/ @andre-richter @slovdahl @viiru- /tests/components/vallox/ @andre-richter @slovdahl @viiru- @yozik04
/homeassistant/components/valve/ @home-assistant/core /homeassistant/components/valve/ @home-assistant/core
/tests/components/valve/ @home-assistant/core /tests/components/valve/ @home-assistant/core
/homeassistant/components/velbus/ @Cereal2nd @brefra /homeassistant/components/velbus/ @Cereal2nd @brefra

View File

@ -1,20 +1,11 @@
"""Support for Vallox ventilation units.""" """Support for Vallox ventilation units."""
from __future__ import annotations from __future__ import annotations
from dataclasses import dataclass, field
from datetime import date
import ipaddress import ipaddress
import logging import logging
from typing import Any, NamedTuple from typing import NamedTuple
from uuid import UUID
from vallox_websocket_api import PROFILE as VALLOX_PROFILE, Vallox, ValloxApiException from vallox_websocket_api import MetricData, Profile, Vallox, ValloxApiException
from vallox_websocket_api.vallox import (
get_model as _api_get_model,
get_next_filter_change_date as _api_get_next_filter_change_date,
get_sw_version as _api_get_sw_version,
get_uuid as _api_get_uuid,
)
import voluptuous as vol import voluptuous as vol
from homeassistant.config_entries import ConfigEntry from homeassistant.config_entries import ConfigEntry
@ -22,7 +13,6 @@ from homeassistant.const import CONF_HOST, CONF_NAME, Platform
from homeassistant.core import HomeAssistant, ServiceCall from homeassistant.core import HomeAssistant, ServiceCall
from homeassistant.helpers import config_validation as cv from homeassistant.helpers import config_validation as cv
from homeassistant.helpers.device_registry import DeviceInfo from homeassistant.helpers.device_registry import DeviceInfo
from homeassistant.helpers.typing import StateType
from homeassistant.helpers.update_coordinator import ( from homeassistant.helpers.update_coordinator import (
CoordinatorEntity, CoordinatorEntity,
DataUpdateCoordinator, DataUpdateCoordinator,
@ -35,9 +25,6 @@ from .const import (
DEFAULT_FAN_SPEED_HOME, DEFAULT_FAN_SPEED_HOME,
DEFAULT_NAME, DEFAULT_NAME,
DOMAIN, DOMAIN,
METRIC_KEY_PROFILE_FAN_SPEED_AWAY,
METRIC_KEY_PROFILE_FAN_SPEED_BOOST,
METRIC_KEY_PROFILE_FAN_SPEED_HOME,
STATE_SCAN_INTERVAL, STATE_SCAN_INTERVAL,
) )
@ -104,58 +91,7 @@ SERVICE_TO_METHOD = {
} }
@dataclass class ValloxDataUpdateCoordinator(DataUpdateCoordinator[MetricData]): # pylint: disable=hass-enforce-coordinator-module
class ValloxState:
"""Describes the current state of the unit."""
metric_cache: dict[str, Any] = field(default_factory=dict)
profile: VALLOX_PROFILE = VALLOX_PROFILE.NONE
def get_metric(self, metric_key: str) -> StateType:
"""Return cached state value."""
if (value := self.metric_cache.get(metric_key)) is None:
return None
if not isinstance(value, (str, int, float)):
return None
return value
@property
def model(self) -> str | None:
"""Return the model, if any."""
model = _api_get_model(self.metric_cache)
if model == "Unknown":
return None
return model
@property
def sw_version(self) -> str:
"""Return the SW version."""
return _api_get_sw_version(self.metric_cache)
@property
def uuid(self) -> UUID | None:
"""Return cached UUID value."""
uuid = _api_get_uuid(self.metric_cache)
if not isinstance(uuid, UUID):
raise TypeError
return uuid
def get_next_filter_change_date(self) -> date | None:
"""Return the next filter change date."""
next_filter_change_date = _api_get_next_filter_change_date(self.metric_cache)
if not isinstance(next_filter_change_date, date):
return None
return next_filter_change_date
class ValloxDataUpdateCoordinator(DataUpdateCoordinator[ValloxState]): # pylint: disable=hass-enforce-coordinator-module
"""The DataUpdateCoordinator for Vallox.""" """The DataUpdateCoordinator for Vallox."""
@ -166,19 +102,15 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
client = Vallox(host) client = Vallox(host)
async def async_update_data() -> ValloxState: async def async_update_data() -> MetricData:
"""Fetch state update.""" """Fetch state update."""
_LOGGER.debug("Updating Vallox state cache") _LOGGER.debug("Updating Vallox state cache")
try: try:
metric_cache = await client.fetch_metrics() return await client.fetch_metric_data()
profile = await client.get_profile()
except ValloxApiException as err: except ValloxApiException as err:
raise UpdateFailed("Error during state cache update") from err raise UpdateFailed("Error during state cache update") from err
return ValloxState(metric_cache, profile)
coordinator = ValloxDataUpdateCoordinator( coordinator = ValloxDataUpdateCoordinator(
hass, hass,
_LOGGER, _LOGGER,
@ -227,7 +159,7 @@ class ValloxServiceHandler:
"""Services implementation.""" """Services implementation."""
def __init__( def __init__(
self, client: Vallox, coordinator: DataUpdateCoordinator[ValloxState] self, client: Vallox, coordinator: DataUpdateCoordinator[MetricData]
) -> None: ) -> None:
"""Initialize the proxy.""" """Initialize the proxy."""
self._client = client self._client = client
@ -240,9 +172,7 @@ class ValloxServiceHandler:
_LOGGER.debug("Setting Home fan speed to: %d%%", fan_speed) _LOGGER.debug("Setting Home fan speed to: %d%%", fan_speed)
try: try:
await self._client.set_values( await self._client.set_fan_speed(Profile.HOME, fan_speed)
{METRIC_KEY_PROFILE_FAN_SPEED_HOME: fan_speed}
)
return True return True
except ValloxApiException as err: except ValloxApiException as err:
@ -256,9 +186,7 @@ class ValloxServiceHandler:
_LOGGER.debug("Setting Away fan speed to: %d%%", fan_speed) _LOGGER.debug("Setting Away fan speed to: %d%%", fan_speed)
try: try:
await self._client.set_values( await self._client.set_fan_speed(Profile.AWAY, fan_speed)
{METRIC_KEY_PROFILE_FAN_SPEED_AWAY: fan_speed}
)
return True return True
except ValloxApiException as err: except ValloxApiException as err:
@ -272,9 +200,7 @@ class ValloxServiceHandler:
_LOGGER.debug("Setting Boost fan speed to: %d%%", fan_speed) _LOGGER.debug("Setting Boost fan speed to: %d%%", fan_speed)
try: try:
await self._client.set_values( await self._client.set_fan_speed(Profile.BOOST, fan_speed)
{METRIC_KEY_PROFILE_FAN_SPEED_BOOST: fan_speed}
)
return True return True
except ValloxApiException as err: except ValloxApiException as err:

View File

@ -38,7 +38,7 @@ class ValloxBinarySensorEntity(ValloxEntity, BinarySensorEntity):
@property @property
def is_on(self) -> bool | None: def is_on(self) -> bool | None:
"""Return true if the binary sensor is on.""" """Return true if the binary sensor is on."""
return self.coordinator.data.get_metric(self.entity_description.metric_key) == 1 return self.coordinator.data.get(self.entity_description.metric_key) == 1
@dataclass(frozen=True) @dataclass(frozen=True)

View File

@ -32,7 +32,7 @@ async def validate_host(hass: HomeAssistant, host: str) -> None:
raise InvalidHost(f"Invalid IP address: {host}") raise InvalidHost(f"Invalid IP address: {host}")
client = Vallox(host) client = Vallox(host)
await client.get_info() await client.fetch_metric_data()
class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):

View File

@ -2,7 +2,7 @@
from datetime import timedelta from datetime import timedelta
from vallox_websocket_api import PROFILE as VALLOX_PROFILE from vallox_websocket_api import Profile as VALLOX_PROFILE
DOMAIN = "vallox" DOMAIN = "vallox"
DEFAULT_NAME = "Vallox" DEFAULT_NAME = "Vallox"
@ -30,8 +30,11 @@ VALLOX_PROFILE_TO_PRESET_MODE_SETTABLE = {
} }
VALLOX_PROFILE_TO_PRESET_MODE_REPORTABLE = { VALLOX_PROFILE_TO_PRESET_MODE_REPORTABLE = {
VALLOX_PROFILE.HOME: "Home",
VALLOX_PROFILE.AWAY: "Away",
VALLOX_PROFILE.BOOST: "Boost",
VALLOX_PROFILE.FIREPLACE: "Fireplace",
VALLOX_PROFILE.EXTRA: "Extra", VALLOX_PROFILE.EXTRA: "Extra",
**VALLOX_PROFILE_TO_PRESET_MODE_SETTABLE,
} }
PRESET_MODE_TO_VALLOX_PROFILE_SETTABLE = { PRESET_MODE_TO_VALLOX_PROFILE_SETTABLE = {

View File

@ -4,12 +4,7 @@ from __future__ import annotations
from collections.abc import Mapping from collections.abc import Mapping
from typing import Any, NamedTuple from typing import Any, NamedTuple
from vallox_websocket_api import ( from vallox_websocket_api import Vallox, ValloxApiException, ValloxInvalidInputException
PROFILE_TO_SET_FAN_SPEED_METRIC_MAP,
Vallox,
ValloxApiException,
ValloxInvalidInputException,
)
from homeassistant.components.fan import FanEntity, FanEntityFeature from homeassistant.components.fan import FanEntity, FanEntityFeature
from homeassistant.config_entries import ConfigEntry from homeassistant.config_entries import ConfigEntry
@ -99,7 +94,7 @@ class ValloxFanEntity(ValloxEntity, FanEntity):
@property @property
def is_on(self) -> bool: def is_on(self) -> bool:
"""Return if device is on.""" """Return if device is on."""
return self.coordinator.data.get_metric(METRIC_KEY_MODE) == MODE_ON return self.coordinator.data.get(METRIC_KEY_MODE) == MODE_ON
@property @property
def preset_mode(self) -> str | None: def preset_mode(self) -> str | None:
@ -112,19 +107,18 @@ class ValloxFanEntity(ValloxEntity, FanEntity):
"""Return the current speed as a percentage.""" """Return the current speed as a percentage."""
vallox_profile = self.coordinator.data.profile vallox_profile = self.coordinator.data.profile
metric_key = PROFILE_TO_SET_FAN_SPEED_METRIC_MAP.get(vallox_profile) try:
if not metric_key: return _convert_to_int(self.coordinator.data.get_fan_speed(vallox_profile))
except ValloxInvalidInputException:
return None return None
return _convert_to_int(self.coordinator.data.get_metric(metric_key))
@property @property
def extra_state_attributes(self) -> Mapping[str, int | None]: def extra_state_attributes(self) -> Mapping[str, int | None]:
"""Return device specific state attributes.""" """Return device specific state attributes."""
data = self.coordinator.data data = self.coordinator.data
return { return {
attr.description: _convert_to_int(data.get_metric(attr.metric_key)) attr.description: _convert_to_int(data.get(attr.metric_key))
for attr in EXTRA_STATE_ATTRIBUTES for attr in EXTRA_STATE_ATTRIBUTES
} }
@ -153,7 +147,9 @@ class ValloxFanEntity(ValloxEntity, FanEntity):
update_needed |= await self._async_set_preset_mode_internal(preset_mode) update_needed |= await self._async_set_preset_mode_internal(preset_mode)
if percentage is not None: if percentage is not None:
update_needed |= await self._async_set_percentage_internal(percentage) update_needed |= await self._async_set_percentage_internal(
percentage, preset_mode
)
if update_needed: if update_needed:
# This state change affects other entities like sensors. Force an immediate update that # This state change affects other entities like sensors. Force an immediate update that
@ -202,19 +198,24 @@ class ValloxFanEntity(ValloxEntity, FanEntity):
try: try:
profile = PRESET_MODE_TO_VALLOX_PROFILE_SETTABLE[preset_mode] profile = PRESET_MODE_TO_VALLOX_PROFILE_SETTABLE[preset_mode]
await self._client.set_profile(profile) await self._client.set_profile(profile)
self.coordinator.data.profile = profile
except ValloxApiException as err: except ValloxApiException as err:
raise HomeAssistantError(f"Failed to set profile: {preset_mode}") from err raise HomeAssistantError(f"Failed to set profile: {preset_mode}") from err
return True return True
async def _async_set_percentage_internal(self, percentage: int) -> bool: async def _async_set_percentage_internal(
self, percentage: int, preset_mode: str | None = None
) -> bool:
"""Set fan speed percentage for current profile. """Set fan speed percentage for current profile.
Returns true if speed has been changed, false otherwise. Returns true if speed has been changed, false otherwise.
""" """
vallox_profile = self.coordinator.data.profile vallox_profile = (
PRESET_MODE_TO_VALLOX_PROFILE_SETTABLE[preset_mode]
if preset_mode is not None
else self.coordinator.data.profile
)
try: try:
await self._client.set_fan_speed(vallox_profile, percentage) await self._client.set_fan_speed(vallox_profile, percentage)

View File

@ -1,10 +1,10 @@
{ {
"domain": "vallox", "domain": "vallox",
"name": "Vallox", "name": "Vallox",
"codeowners": ["@andre-richter", "@slovdahl", "@viiru-"], "codeowners": ["@andre-richter", "@slovdahl", "@viiru-", "@yozik04"],
"config_flow": true, "config_flow": true,
"documentation": "https://www.home-assistant.io/integrations/vallox", "documentation": "https://www.home-assistant.io/integrations/vallox",
"iot_class": "local_polling", "iot_class": "local_polling",
"loggers": ["vallox_websocket_api"], "loggers": ["vallox_websocket_api"],
"requirements": ["vallox-websocket-api==4.0.3"] "requirements": ["vallox-websocket-api==5.0.2"]
} }

View File

@ -44,9 +44,7 @@ class ValloxNumberEntity(ValloxEntity, NumberEntity):
def native_value(self) -> float | None: def native_value(self) -> float | None:
"""Return the value reported by the sensor.""" """Return the value reported by the sensor."""
if ( if (
value := self.coordinator.data.get_metric( value := self.coordinator.data.get(self.entity_description.metric_key)
self.entity_description.metric_key
)
) is None: ) is None:
return None return None

View File

@ -58,7 +58,7 @@ class ValloxSensorEntity(ValloxEntity, SensorEntity):
if (metric_key := self.entity_description.metric_key) is None: if (metric_key := self.entity_description.metric_key) is None:
return None return None
value = self.coordinator.data.get_metric(metric_key) value = self.coordinator.data.get(metric_key)
if self.entity_description.round_ndigits is not None and isinstance( if self.entity_description.round_ndigits is not None and isinstance(
value, float value, float
@ -90,7 +90,7 @@ class ValloxFanSpeedSensor(ValloxSensorEntity):
@property @property
def native_value(self) -> StateType | datetime: def native_value(self) -> StateType | datetime:
"""Return the value reported by the sensor.""" """Return the value reported by the sensor."""
fan_is_on = self.coordinator.data.get_metric(METRIC_KEY_MODE) == MODE_ON fan_is_on = self.coordinator.data.get(METRIC_KEY_MODE) == MODE_ON
return super().native_value if fan_is_on else 0 return super().native_value if fan_is_on else 0
@ -100,7 +100,7 @@ class ValloxFilterRemainingSensor(ValloxSensorEntity):
@property @property
def native_value(self) -> StateType | datetime: def native_value(self) -> StateType | datetime:
"""Return the value reported by the sensor.""" """Return the value reported by the sensor."""
next_filter_change_date = self.coordinator.data.get_next_filter_change_date() next_filter_change_date = self.coordinator.data.next_filter_change_date
if next_filter_change_date is None: if next_filter_change_date is None:
return None return None

View File

@ -41,9 +41,7 @@ class ValloxSwitchEntity(ValloxEntity, SwitchEntity):
def is_on(self) -> bool | None: def is_on(self) -> bool | None:
"""Return true if the switch is on.""" """Return true if the switch is on."""
if ( if (
value := self.coordinator.data.get_metric( value := self.coordinator.data.get(self.entity_description.metric_key)
self.entity_description.metric_key
)
) is None: ) is None:
return None return None
return value == 1 return value == 1
@ -93,12 +91,12 @@ async def async_setup_entry(
"""Set up the switches.""" """Set up the switches."""
data = hass.data[DOMAIN][entry.entry_id] data = hass.data[DOMAIN][entry.entry_id]
client = data["client"]
client.set_settable_address("A_CYC_BYPASS_LOCKED", int)
async_add_entities( async_add_entities(
[ [
ValloxSwitchEntity(data["name"], data["coordinator"], description, client) ValloxSwitchEntity(
data["name"], data["coordinator"], description, data["client"]
)
for description in SWITCH_ENTITIES for description in SWITCH_ENTITIES
] ]
) )

View File

@ -2786,7 +2786,7 @@ uvcclient==0.11.0
vacuum-map-parser-roborock==0.1.1 vacuum-map-parser-roborock==0.1.1
# homeassistant.components.vallox # homeassistant.components.vallox
vallox-websocket-api==4.0.3 vallox-websocket-api==5.0.2
# homeassistant.components.rdw # homeassistant.components.rdw
vehicle==2.2.1 vehicle==2.2.1

View File

@ -2130,7 +2130,7 @@ uvcclient==0.11.0
vacuum-map-parser-roborock==0.1.1 vacuum-map-parser-roborock==0.1.1
# homeassistant.components.vallox # homeassistant.components.vallox
vallox-websocket-api==4.0.3 vallox-websocket-api==5.0.2
# homeassistant.components.rdw # homeassistant.components.rdw
vehicle==2.2.1 vehicle==2.2.1

View File

@ -1,13 +1,9 @@
"""Common utilities for Vallox tests.""" """Common utilities for Vallox tests."""
import random from unittest.mock import AsyncMock, patch
import string
from typing import Any
from unittest.mock import patch
from uuid import UUID
import pytest import pytest
from vallox_websocket_api.vallox import PROFILE from vallox_websocket_api import MetricData
from homeassistant.components.vallox.const import DOMAIN from homeassistant.components.vallox.const import DOMAIN
from homeassistant.const import CONF_HOST, CONF_NAME from homeassistant.const import CONF_HOST, CONF_NAME
@ -31,83 +27,80 @@ def mock_entry(hass: HomeAssistant) -> MockConfigEntry:
return vallox_mock_entry return vallox_mock_entry
def patch_metrics(metrics: dict[str, Any]): @pytest.fixture
def default_metrics():
"""Return default Vallox metrics."""
return {
"A_CYC_MACHINE_MODEL": 3,
"A_CYC_APPL_SW_VERSION_1": 2,
"A_CYC_APPL_SW_VERSION_2": 0,
"A_CYC_APPL_SW_VERSION_3": 16,
"A_CYC_UUID0": 5,
"A_CYC_UUID1": 6,
"A_CYC_UUID2": 7,
"A_CYC_UUID3": 8,
"A_CYC_UUID4": 9,
"A_CYC_UUID5": 10,
"A_CYC_UUID6": 11,
"A_CYC_UUID7": 12,
"A_CYC_BOOST_TIMER": 30,
"A_CYC_FIREPLACE_TIMER": 30,
"A_CYC_EXTRA_TIMER": 30,
"A_CYC_MODE": 0,
"A_CYC_STATE": 0,
"A_CYC_FILTER_CHANGED_YEAR": 24,
"A_CYC_FILTER_CHANGED_MONTH": 2,
"A_CYC_FILTER_CHANGED_DAY": 16,
"A_CYC_FILTER_CHANGE_INTERVAL": 120,
"A_CYC_TOTAL_FAULT_COUNT": 0,
"A_CYC_FAULT_CODE": 0,
"A_CYC_FAULT_ACTIVITY": 0,
"A_CYC_FAULT_FIRST_DATE": 0,
"A_CYC_FAULT_LAST_DATE": 0,
"A_CYC_FAULT_SEVERITY": 0,
"A_CYC_FAULT_COUNT": 0,
"A_CYC_HOME_SPEED_SETTING": 30,
"A_CYC_AWAY_SPEED_SETTING": 10,
"A_CYC_BOOST_SPEED_SETTING": 80,
}
@pytest.fixture(autouse=True)
def fetch_metric_data_mock(default_metrics):
"""Stub the Vallox fetch_metric_data method."""
with patch(
"homeassistant.components.vallox.Vallox.fetch_metric_data",
new_callable=AsyncMock,
) as mock:
mock.return_value = MetricData(default_metrics)
yield mock
@pytest.fixture
def setup_fetch_metric_data_mock(fetch_metric_data_mock, default_metrics):
"""Patch the Vallox metrics response.""" """Patch the Vallox metrics response."""
return patch(
"homeassistant.components.vallox.Vallox.fetch_metrics", def _setup(metrics=None, metric_data_class=MetricData):
return_value=metrics, metrics = metrics or {}
fetch_metric_data_mock.return_value = metric_data_class(
{**default_metrics, **metrics}
) )
return fetch_metric_data_mock
def patch_profile(profile: PROFILE): return _setup
"""Patch the Vallox metrics response."""
return patch(
"homeassistant.components.vallox.Vallox.get_profile",
return_value=profile,
)
def patch_profile_set(): def patch_set_profile():
"""Patch the Vallox metrics set values.""" """Patch the Vallox metrics set values."""
return patch("homeassistant.components.vallox.Vallox.set_profile") return patch("homeassistant.components.vallox.Vallox.set_profile")
def patch_metrics_set(): def patch_set_fan_speed():
"""Patch the Vallox metrics set values."""
return patch("homeassistant.components.vallox.Vallox.set_fan_speed")
def patch_set_values():
"""Patch the Vallox metrics set values.""" """Patch the Vallox metrics set values."""
return patch("homeassistant.components.vallox.Vallox.set_values") return patch("homeassistant.components.vallox.Vallox.set_values")
@pytest.fixture(autouse=True)
def patch_empty_metrics():
"""Patch the Vallox profile response."""
with patch(
"homeassistant.components.vallox.Vallox.fetch_metrics",
return_value={},
):
yield
@pytest.fixture(autouse=True)
def patch_default_profile():
"""Patch the Vallox profile response."""
with patch(
"homeassistant.components.vallox.Vallox.get_profile",
return_value=PROFILE.HOME,
):
yield
@pytest.fixture(autouse=True)
def patch_model():
"""Patch the Vallox model response."""
with patch(
"homeassistant.components.vallox._api_get_model",
return_value="Vallox Testmodel",
):
yield
@pytest.fixture(autouse=True)
def patch_sw_version():
"""Patch the Vallox SW version response."""
with patch(
"homeassistant.components.vallox._api_get_sw_version",
return_value="0.1.2",
):
yield
@pytest.fixture(autouse=True)
def patch_uuid():
"""Patch the Vallox UUID response."""
with patch(
"homeassistant.components.vallox._api_get_uuid",
return_value=_random_uuid(),
):
yield
def _random_uuid():
"""Generate a random UUID."""
uuid = "".join(random.choices(string.hexdigits, k=32))
return UUID(uuid)

View File

@ -5,8 +5,6 @@ import pytest
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from .conftest import patch_metrics
from tests.common import MockConfigEntry from tests.common import MockConfigEntry
@ -21,14 +19,19 @@ async def test_binary_sensor_entitity(
metrics: dict[str, Any], metrics: dict[str, Any],
expected_state: str, expected_state: str,
mock_entry: MockConfigEntry, mock_entry: MockConfigEntry,
setup_fetch_metric_data_mock,
hass: HomeAssistant, hass: HomeAssistant,
): ) -> None:
"""Test binary sensor with metrics.""" """Test binary sensor with metrics."""
# Arrange
fetch_metric_data_mock = setup_fetch_metric_data_mock(metrics)
# Act # Act
with patch_metrics(metrics=metrics):
await hass.config_entries.async_setup(mock_entry.entry_id) await hass.config_entries.async_setup(mock_entry.entry_id)
await hass.async_block_till_done() await hass.async_block_till_done()
# Assert # Assert
fetch_metric_data_mock.assert_called_once()
sensor = hass.states.get("binary_sensor.vallox_post_heater") sensor = hass.states.get("binary_sensor.vallox_post_heater")
assert sensor.state == expected_state assert sensor.state == expected_state

View File

@ -33,7 +33,7 @@ async def test_form_create_entry(hass: HomeAssistant) -> None:
assert init["errors"] is None assert init["errors"] is None
with patch( with patch(
"homeassistant.components.vallox.config_flow.Vallox.get_info", "homeassistant.components.vallox.config_flow.Vallox.fetch_metric_data",
return_value=None, return_value=None,
), patch( ), patch(
"homeassistant.components.vallox.async_setup_entry", "homeassistant.components.vallox.async_setup_entry",
@ -74,7 +74,7 @@ async def test_form_vallox_api_exception_cannot_connect(hass: HomeAssistant) ->
) )
with patch( with patch(
"homeassistant.components.vallox.config_flow.Vallox.get_info", "homeassistant.components.vallox.config_flow.Vallox.fetch_metric_data",
side_effect=ValloxApiException, side_effect=ValloxApiException,
): ):
result = await hass.config_entries.flow.async_configure( result = await hass.config_entries.flow.async_configure(
@ -94,7 +94,7 @@ async def test_form_os_error_cannot_connect(hass: HomeAssistant) -> None:
) )
with patch( with patch(
"homeassistant.components.vallox.config_flow.Vallox.get_info", "homeassistant.components.vallox.config_flow.Vallox.fetch_metric_data",
side_effect=ValloxWebsocketException, side_effect=ValloxWebsocketException,
): ):
result = await hass.config_entries.flow.async_configure( result = await hass.config_entries.flow.async_configure(
@ -114,7 +114,7 @@ async def test_form_unknown_exception(hass: HomeAssistant) -> None:
) )
with patch( with patch(
"homeassistant.components.vallox.config_flow.Vallox.get_info", "homeassistant.components.vallox.config_flow.Vallox.fetch_metric_data",
side_effect=Exception, side_effect=Exception,
): ):
result = await hass.config_entries.flow.async_configure( result = await hass.config_entries.flow.async_configure(

View File

@ -2,7 +2,7 @@
from unittest.mock import call from unittest.mock import call
import pytest import pytest
from vallox_websocket_api import PROFILE, ValloxApiException from vallox_websocket_api import MetricData, MetricValue, Profile, ValloxApiException
from homeassistant.components.fan import ( from homeassistant.components.fan import (
ATTR_PERCENTAGE, ATTR_PERCENTAGE,
@ -16,7 +16,7 @@ from homeassistant.const import ATTR_ENTITY_ID, SERVICE_TURN_OFF, SERVICE_TURN_O
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from homeassistant.exceptions import HomeAssistantError from homeassistant.exceptions import HomeAssistantError
from .conftest import patch_metrics, patch_metrics_set, patch_profile, patch_profile_set from .conftest import patch_set_fan_speed, patch_set_profile, patch_set_values
from tests.common import MockConfigEntry from tests.common import MockConfigEntry
@ -26,43 +26,55 @@ from tests.common import MockConfigEntry
[({"A_CYC_MODE": 0}, "on"), ({"A_CYC_MODE": 5}, "off")], [({"A_CYC_MODE": 0}, "on"), ({"A_CYC_MODE": 5}, "off")],
) )
async def test_fan_state( async def test_fan_state(
metrics: dict[str, int], metrics: dict[str, MetricValue],
expected_state: str, expected_state: str,
mock_entry: MockConfigEntry, mock_entry: MockConfigEntry,
setup_fetch_metric_data_mock,
hass: HomeAssistant, hass: HomeAssistant,
) -> None: ) -> None:
"""Test fan on/off state.""" """Test fan on/off state."""
# Arrange
fetch_metric_data_mock = setup_fetch_metric_data_mock(metrics=metrics)
# Act # Act
with patch_metrics(metrics=metrics):
await hass.config_entries.async_setup(mock_entry.entry_id) await hass.config_entries.async_setup(mock_entry.entry_id)
await hass.async_block_till_done() await hass.async_block_till_done()
# Assert # Assert
fetch_metric_data_mock.assert_called_once()
sensor = hass.states.get("fan.vallox") sensor = hass.states.get("fan.vallox")
assert sensor assert sensor
assert sensor.state == expected_state assert sensor.state == expected_state
@pytest.mark.parametrize( @pytest.mark.parametrize(
("profile", "expected_preset"), ("vallox_profile", "expected_preset"),
[ [
(PROFILE.HOME, "Home"), (Profile.HOME, "Home"),
(PROFILE.AWAY, "Away"), (Profile.AWAY, "Away"),
(PROFILE.BOOST, "Boost"), (Profile.BOOST, "Boost"),
(PROFILE.FIREPLACE, "Fireplace"), (Profile.FIREPLACE, "Fireplace"),
], ],
) )
async def test_fan_profile( async def test_fan_profile(
profile: PROFILE, vallox_profile: Profile,
expected_preset: str, expected_preset: str,
mock_entry: MockConfigEntry, mock_entry: MockConfigEntry,
setup_fetch_metric_data_mock,
hass: HomeAssistant, hass: HomeAssistant,
) -> None: ) -> None:
"""Test fan profile.""" """Test fan profile."""
# Arrange
class MockMetricData(MetricData):
@property
def profile(self):
return vallox_profile
setup_fetch_metric_data_mock(metric_data_class=MockMetricData)
# Act # Act
with patch_profile(profile):
await hass.config_entries.async_setup(mock_entry.entry_id) await hass.config_entries.async_setup(mock_entry.entry_id)
await hass.async_block_till_done() await hass.async_block_till_done()
@ -81,13 +93,16 @@ async def test_fan_profile(
) )
async def test_turn_on_off( async def test_turn_on_off(
service: str, service: str,
initial_metrics: dict[str, int], initial_metrics: dict[str, MetricValue],
expected_called_with: dict[str, int], expected_called_with: dict[str, MetricValue],
mock_entry: MockConfigEntry, mock_entry: MockConfigEntry,
setup_fetch_metric_data_mock,
hass: HomeAssistant, hass: HomeAssistant,
) -> None: ) -> None:
"""Test turn on/off.""" """Test turn on/off."""
with patch_metrics(metrics=initial_metrics), patch_metrics_set() as metrics_set: setup_fetch_metric_data_mock(metrics=initial_metrics)
with patch_set_values() as set_values:
await hass.config_entries.async_setup(mock_entry.entry_id) await hass.config_entries.async_setup(mock_entry.entry_id)
await hass.async_block_till_done() await hass.async_block_till_done()
await hass.services.async_call( await hass.services.async_call(
@ -96,7 +111,7 @@ async def test_turn_on_off(
service_data={ATTR_ENTITY_ID: "fan.vallox"}, service_data={ATTR_ENTITY_ID: "fan.vallox"},
blocking=True, blocking=True,
) )
metrics_set.assert_called_once_with(expected_called_with) set_values.assert_called_once_with(expected_called_with)
@pytest.mark.parametrize( @pytest.mark.parametrize(
@ -118,15 +133,17 @@ async def test_turn_on_off(
], ],
) )
async def test_turn_on_with_parameters( async def test_turn_on_with_parameters(
initial_metrics: dict[str, int], initial_metrics: dict[str, MetricValue],
expected_call_args_list: list[tuple], expected_call_args_list: list[tuple],
mock_entry: MockConfigEntry, mock_entry: MockConfigEntry,
hass: HomeAssistant, hass: HomeAssistant,
setup_fetch_metric_data_mock,
) -> None: ) -> None:
"""Test turn on/off.""" """Test turn on/off."""
with patch_metrics(
metrics=initial_metrics setup_fetch_metric_data_mock(metrics=initial_metrics)
), patch_metrics_set() as metrics_set, patch_profile_set() as profile_set:
with patch_set_values() as set_values, patch_set_profile() as set_profile:
await hass.config_entries.async_setup(mock_entry.entry_id) await hass.config_entries.async_setup(mock_entry.entry_id)
await hass.async_block_till_done() await hass.async_block_till_done()
await hass.services.async_call( await hass.services.async_call(
@ -139,29 +156,38 @@ async def test_turn_on_with_parameters(
}, },
blocking=True, blocking=True,
) )
assert metrics_set.call_args_list == expected_call_args_list set_profile.assert_called_once_with(Profile.AWAY)
profile_set.assert_called_once_with(PROFILE.AWAY) assert set_values.call_args_list == expected_call_args_list
@pytest.mark.parametrize( @pytest.mark.parametrize(
("preset", "initial_profile", "expected_call_args_list"), ("preset", "initial_profile", "expected_call_args_list"),
[ [
("Home", PROFILE.AWAY, [call(PROFILE.HOME)]), ("Home", Profile.AWAY, [call(Profile.HOME)]),
("Away", PROFILE.HOME, [call(PROFILE.AWAY)]), ("Away", Profile.HOME, [call(Profile.AWAY)]),
("Boost", PROFILE.HOME, [call(PROFILE.BOOST)]), ("Boost", Profile.HOME, [call(Profile.BOOST)]),
("Fireplace", PROFILE.HOME, [call(PROFILE.FIREPLACE)]), ("Fireplace", Profile.HOME, [call(Profile.FIREPLACE)]),
("Home", PROFILE.HOME, []), ("Home", Profile.HOME, []), # No change
], ],
) )
async def test_set_preset_mode( async def test_set_preset_mode(
preset: str, preset: str,
initial_profile: PROFILE, initial_profile: Profile,
expected_call_args_list: list[tuple], expected_call_args_list: list[tuple],
mock_entry: MockConfigEntry, mock_entry: MockConfigEntry,
hass: HomeAssistant, hass: HomeAssistant,
setup_fetch_metric_data_mock,
) -> None: ) -> None:
"""Test set preset mode.""" """Test set preset mode."""
with patch_profile(initial_profile), patch_profile_set() as profile_set:
class MockMetricData(MetricData):
@property
def profile(self):
return initial_profile
setup_fetch_metric_data_mock(metric_data_class=MockMetricData)
with patch_set_profile() as set_profile:
await hass.config_entries.async_setup(mock_entry.entry_id) await hass.config_entries.async_setup(mock_entry.entry_id)
await hass.async_block_till_done() await hass.async_block_till_done()
await hass.services.async_call( await hass.services.async_call(
@ -170,7 +196,7 @@ async def test_set_preset_mode(
service_data={ATTR_ENTITY_ID: "fan.vallox", ATTR_PRESET_MODE: preset}, service_data={ATTR_ENTITY_ID: "fan.vallox", ATTR_PRESET_MODE: preset},
blocking=True, blocking=True,
) )
assert profile_set.call_args_list == expected_call_args_list assert set_profile.call_args_list == expected_call_args_list
async def test_set_invalid_preset_mode( async def test_set_invalid_preset_mode(
@ -198,8 +224,8 @@ async def test_set_preset_mode_exception(
hass: HomeAssistant, hass: HomeAssistant,
) -> None: ) -> None:
"""Test set preset mode.""" """Test set preset mode."""
with patch_profile_set() as profile_set: with patch_set_profile() as set_profile:
profile_set.side_effect = ValloxApiException("Fake exception") set_profile.side_effect = ValloxApiException("Fake exception")
await hass.config_entries.async_setup(mock_entry.entry_id) await hass.config_entries.async_setup(mock_entry.entry_id)
await hass.async_block_till_done() await hass.async_block_till_done()
with pytest.raises(HomeAssistantError): with pytest.raises(HomeAssistantError):
@ -212,25 +238,40 @@ async def test_set_preset_mode_exception(
@pytest.mark.parametrize( @pytest.mark.parametrize(
("profile", "percentage", "expected_call_args_list"), (
"initial_profile",
"percentage",
"expected_set_fan_speed_call",
"expected_set_values_call",
),
[ [
(PROFILE.HOME, 40, [call({"A_CYC_HOME_SPEED_SETTING": 40})]), (Profile.HOME, 40, [call(Profile.HOME, 40)], []),
(PROFILE.AWAY, 30, [call({"A_CYC_AWAY_SPEED_SETTING": 30})]), (Profile.AWAY, 30, [call(Profile.AWAY, 30)], []),
(PROFILE.BOOST, 60, [call({"A_CYC_BOOST_SPEED_SETTING": 60})]), (Profile.BOOST, 60, [call(Profile.BOOST, 60)], []),
(PROFILE.HOME, 0, [call({"A_CYC_MODE": 5})]), (Profile.HOME, 0, [], [call({"A_CYC_MODE": 5})]), # Turn off
], ],
) )
async def test_set_fan_speed( async def test_set_fan_speed(
profile: PROFILE, initial_profile: Profile,
percentage: int, percentage: int,
expected_call_args_list: list[tuple], expected_set_fan_speed_call: list[tuple],
expected_set_values_call: list[tuple],
mock_entry: MockConfigEntry, mock_entry: MockConfigEntry,
hass: HomeAssistant, hass: HomeAssistant,
setup_fetch_metric_data_mock,
) -> None: ) -> None:
"""Test set fan speed percentage.""" """Test set fan speed percentage."""
with patch_profile(profile), patch_metrics_set() as metrics_set, patch_metrics(
{"A_CYC_MODE": 0} class MockMetricData(MetricData):
): @property
def profile(self):
return initial_profile
setup_fetch_metric_data_mock(
metrics={"A_CYC_MODE": 0}, metric_data_class=MockMetricData
)
with patch_set_fan_speed() as set_fan_speed, patch_set_values() as set_values:
await hass.config_entries.async_setup(mock_entry.entry_id) await hass.config_entries.async_setup(mock_entry.entry_id)
await hass.async_block_till_done() await hass.async_block_till_done()
await hass.services.async_call( await hass.services.async_call(
@ -239,18 +280,20 @@ async def test_set_fan_speed(
service_data={ATTR_ENTITY_ID: "fan.vallox", ATTR_PERCENTAGE: percentage}, service_data={ATTR_ENTITY_ID: "fan.vallox", ATTR_PERCENTAGE: percentage},
blocking=True, blocking=True,
) )
assert metrics_set.call_args_list == expected_call_args_list assert set_fan_speed.call_args_list == expected_set_fan_speed_call
assert set_values.call_args_list == expected_set_values_call
async def test_set_fan_speed_exception( async def test_set_fan_speed_exception(
mock_entry: MockConfigEntry, mock_entry: MockConfigEntry, hass: HomeAssistant, setup_fetch_metric_data_mock
hass: HomeAssistant,
) -> None: ) -> None:
"""Test set fan speed percentage.""" """Test set fan speed percentage."""
with patch_metrics_set() as metrics_set, patch_metrics( setup_fetch_metric_data_mock(
{"A_CYC_MODE": 0, "A_CYC_HOME_SPEED_SETTING": 30} metrics={"A_CYC_MODE": 0, "A_CYC_HOME_SPEED_SETTING": 30}
): )
metrics_set.side_effect = ValloxApiException("Fake failure")
with patch_set_values() as set_values:
set_values.side_effect = ValloxApiException("Fake failure")
await hass.config_entries.async_setup(mock_entry.entry_id) await hass.config_entries.async_setup(mock_entry.entry_id)
await hass.async_block_till_done() await hass.async_block_till_done()
with pytest.raises(HomeAssistantError): with pytest.raises(HomeAssistantError):

View File

@ -0,0 +1,49 @@
"""Tests for the Vallox integration."""
import pytest
from vallox_websocket_api import Profile
from homeassistant.components.vallox import (
ATTR_PROFILE_FAN_SPEED,
SERVICE_SET_PROFILE_FAN_SPEED_AWAY,
SERVICE_SET_PROFILE_FAN_SPEED_BOOST,
SERVICE_SET_PROFILE_FAN_SPEED_HOME,
)
from homeassistant.components.vallox.const import DOMAIN
from homeassistant.core import HomeAssistant
from .conftest import patch_set_fan_speed
from tests.common import MockConfigEntry
@pytest.mark.parametrize(
("service", "profile"),
[
(SERVICE_SET_PROFILE_FAN_SPEED_HOME, Profile.HOME),
(SERVICE_SET_PROFILE_FAN_SPEED_AWAY, Profile.AWAY),
(SERVICE_SET_PROFILE_FAN_SPEED_BOOST, Profile.BOOST),
],
)
async def test_create_service(
hass: HomeAssistant,
mock_entry: MockConfigEntry,
service: str,
profile: Profile,
) -> None:
"""Test services for setting fan speed."""
# Act
await hass.config_entries.async_setup(mock_entry.entry_id)
await hass.async_block_till_done()
with patch_set_fan_speed() as set_fan_speed:
await hass.services.async_call(
DOMAIN,
service,
service_data={ATTR_PROFILE_FAN_SPEED: 30},
)
await hass.async_block_till_done()
# Assert
set_fan_speed.assert_called_once_with(profile, 30)

View File

@ -9,7 +9,7 @@ from homeassistant.components.number.const import (
from homeassistant.const import ATTR_ENTITY_ID from homeassistant.const import ATTR_ENTITY_ID
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from .conftest import patch_metrics, patch_metrics_set from .conftest import patch_set_values
from tests.common import MockConfigEntry from tests.common import MockConfigEntry
@ -41,13 +41,13 @@ async def test_temperature_number_entities(
value: float, value: float,
mock_entry: MockConfigEntry, mock_entry: MockConfigEntry,
hass: HomeAssistant, hass: HomeAssistant,
setup_fetch_metric_data_mock,
) -> None: ) -> None:
"""Test temperature entities.""" """Test temperature entities."""
# Arrange # Arrange
metrics = {metric_key: value} setup_fetch_metric_data_mock(metrics={metric_key: value})
# Act # Act
with patch_metrics(metrics=metrics):
await hass.config_entries.async_setup(mock_entry.entry_id) await hass.config_entries.async_setup(mock_entry.entry_id)
await hass.async_block_till_done() await hass.async_block_till_done()
@ -66,10 +66,14 @@ async def test_temperature_number_entity_set(
value: float, value: float,
mock_entry: MockConfigEntry, mock_entry: MockConfigEntry,
hass: HomeAssistant, hass: HomeAssistant,
setup_fetch_metric_data_mock,
) -> None: ) -> None:
"""Test temperature set.""" """Test temperature set."""
# Arrange
setup_fetch_metric_data_mock(metrics={metric_key: value})
# Act # Act
with patch_metrics(metrics={}), patch_metrics_set() as metrics_set: with patch_set_values() as set_values:
await hass.config_entries.async_setup(mock_entry.entry_id) await hass.config_entries.async_setup(mock_entry.entry_id)
await hass.async_block_till_done() await hass.async_block_till_done()
await hass.services.async_call( await hass.services.async_call(
@ -81,4 +85,4 @@ async def test_temperature_number_entity_set(
}, },
) )
await hass.async_block_till_done() await hass.async_block_till_done()
metrics_set.assert_called_once_with({metric_key: value}) set_values.assert_called_once_with({metric_key: value})

View File

@ -1,15 +1,13 @@
"""Tests for Vallox sensor platform.""" """Tests for Vallox sensor platform."""
from datetime import datetime, timedelta, tzinfo from datetime import datetime, timedelta, tzinfo
from unittest.mock import patch
import pytest import pytest
from vallox_websocket_api import MetricData
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from homeassistant.util import dt as dt_util from homeassistant.util import dt as dt_util
from .conftest import patch_metrics
from tests.common import MockConfigEntry from tests.common import MockConfigEntry
@ -45,32 +43,19 @@ def _now_at_13():
return dt_util.now().timetz().replace(hour=13, minute=0, second=0, microsecond=0) return dt_util.now().timetz().replace(hour=13, minute=0, second=0, microsecond=0)
async def test_remaining_filter_returns_timestamp(
mock_entry: MockConfigEntry, hass: HomeAssistant
) -> None:
"""Test that the remaining time for filter sensor returns a timestamp."""
# Act
with patch(
"homeassistant.components.vallox._api_get_next_filter_change_date",
return_value=dt_util.now().date(),
), patch_metrics(metrics={}):
await hass.config_entries.async_setup(mock_entry.entry_id)
await hass.async_block_till_done()
# Assert
sensor = hass.states.get("sensor.vallox_remaining_time_for_filter")
assert sensor.attributes["device_class"] == "timestamp"
async def test_remaining_time_for_filter_none_returned_from_vallox( async def test_remaining_time_for_filter_none_returned_from_vallox(
mock_entry: MockConfigEntry, hass: HomeAssistant mock_entry: MockConfigEntry, hass: HomeAssistant, setup_fetch_metric_data_mock
) -> None: ) -> None:
"""Test that the remaining time for filter sensor returns 'unknown' when Vallox returns None.""" """Test that the remaining time for filter sensor returns 'unknown' when Vallox returns None."""
class MockMetricData(MetricData):
@property
def next_filter_change_date(self):
return None
# Arrange
setup_fetch_metric_data_mock(metric_data_class=MockMetricData)
# Act # Act
with patch(
"homeassistant.components.vallox._api_get_next_filter_change_date",
return_value=None,
), patch_metrics(metrics={}):
await hass.config_entries.async_setup(mock_entry.entry_id) await hass.config_entries.async_setup(mock_entry.entry_id)
await hass.async_block_till_done() await hass.async_block_till_done()
@ -80,166 +65,72 @@ async def test_remaining_time_for_filter_none_returned_from_vallox(
@pytest.mark.parametrize( @pytest.mark.parametrize(
"set_tz", ("remaining_days", "set_tz"),
[ [
"utc", (112, "utc"),
"helsinki", (112, "helsinki"),
"new_york", (112, "new_york"),
(0, "utc"),
(-3, "utc"),
], ],
indirect=True, indirect=["set_tz"],
) )
async def test_remaining_time_for_filter_in_the_future( async def test_remaining_time_for_filter(
mock_entry: MockConfigEntry, set_tz: tzinfo, hass: HomeAssistant remaining_days,
set_tz: tzinfo,
mock_entry: MockConfigEntry,
hass: HomeAssistant,
setup_fetch_metric_data_mock,
) -> None: ) -> None:
"""Test remaining time for filter when Vallox returns a date in the future.""" """Test remaining time for filter when Vallox returns different dates."""
# Arrange # Arrange
remaining_days = 112
mocked_filter_end_date = dt_util.now().date() + timedelta(days=remaining_days) mocked_filter_end_date = dt_util.now().date() + timedelta(days=remaining_days)
class MockMetricData(MetricData):
@property
def next_filter_change_date(self):
return mocked_filter_end_date
setup_fetch_metric_data_mock(metric_data_class=MockMetricData)
# Act # Act
with patch(
"homeassistant.components.vallox._api_get_next_filter_change_date",
return_value=mocked_filter_end_date,
), patch_metrics(metrics={}):
await hass.config_entries.async_setup(mock_entry.entry_id) await hass.config_entries.async_setup(mock_entry.entry_id)
await hass.async_block_till_done() await hass.async_block_till_done()
# Assert # Assert
sensor = hass.states.get("sensor.vallox_remaining_time_for_filter") sensor = hass.states.get("sensor.vallox_remaining_time_for_filter")
assert sensor.attributes["device_class"] == "timestamp"
assert _sensor_to_datetime(sensor) == datetime.combine( assert _sensor_to_datetime(sensor) == datetime.combine(
mocked_filter_end_date, mocked_filter_end_date,
_now_at_13(), _now_at_13(),
) )
async def test_remaining_time_for_filter_today( @pytest.mark.parametrize(
mock_entry: MockConfigEntry, hass: HomeAssistant ("metrics", "expected_state"),
) -> None: [
"""Test remaining time for filter when Vallox returns today.""" ({"A_CYC_CELL_STATE": 0}, "Heat Recovery"),
# Arrange ({"A_CYC_CELL_STATE": 1}, "Cool Recovery"),
remaining_days = 0 ({"A_CYC_CELL_STATE": 2}, "Bypass"),
mocked_filter_end_date = dt_util.now().date() + timedelta(days=remaining_days) ({"A_CYC_CELL_STATE": 3}, "Defrosting"),
({"A_CYC_CELL_STATE": 4}, "unknown"),
# Act ],
with patch(
"homeassistant.components.vallox._api_get_next_filter_change_date",
return_value=mocked_filter_end_date,
), patch_metrics(metrics={}):
await hass.config_entries.async_setup(mock_entry.entry_id)
await hass.async_block_till_done()
# Assert
sensor = hass.states.get("sensor.vallox_remaining_time_for_filter")
assert _sensor_to_datetime(sensor) == datetime.combine(
mocked_filter_end_date,
_now_at_13(),
) )
async def test_cell_state_sensor(
metrics,
async def test_remaining_time_for_filter_in_the_past( expected_state,
mock_entry: MockConfigEntry, hass: HomeAssistant mock_entry: MockConfigEntry,
hass: HomeAssistant,
setup_fetch_metric_data_mock,
) -> None: ) -> None:
"""Test remaining time for filter when Vallox returns a date in the past.""" """Test cell state sensor in different states."""
# Arrange # Arrange
remaining_days = -3 setup_fetch_metric_data_mock(metrics=metrics)
mocked_filter_end_date = dt_util.now().date() + timedelta(days=remaining_days)
# Act # Act
with patch(
"homeassistant.components.vallox._api_get_next_filter_change_date",
return_value=mocked_filter_end_date,
), patch_metrics(metrics={}):
await hass.config_entries.async_setup(mock_entry.entry_id)
await hass.async_block_till_done()
# Assert
sensor = hass.states.get("sensor.vallox_remaining_time_for_filter")
assert _sensor_to_datetime(sensor) == datetime.combine(
mocked_filter_end_date,
_now_at_13(),
)
async def test_cell_state_sensor_heat_recovery(
mock_entry: MockConfigEntry, hass: HomeAssistant
) -> None:
"""Test cell state sensor in heat recovery state."""
# Arrange
metrics = {"A_CYC_CELL_STATE": 0}
# Act
with patch_metrics(metrics=metrics):
await hass.config_entries.async_setup(mock_entry.entry_id) await hass.config_entries.async_setup(mock_entry.entry_id)
await hass.async_block_till_done() await hass.async_block_till_done()
# Assert # Assert
sensor = hass.states.get("sensor.vallox_cell_state") sensor = hass.states.get("sensor.vallox_cell_state")
assert sensor.state == "Heat Recovery" assert sensor.state == expected_state
async def test_cell_state_sensor_cool_recovery(
mock_entry: MockConfigEntry, hass: HomeAssistant
) -> None:
"""Test cell state sensor in cool recovery state."""
# Arrange
metrics = {"A_CYC_CELL_STATE": 1}
# Act
with patch_metrics(metrics=metrics):
await hass.config_entries.async_setup(mock_entry.entry_id)
await hass.async_block_till_done()
# Assert
sensor = hass.states.get("sensor.vallox_cell_state")
assert sensor.state == "Cool Recovery"
async def test_cell_state_sensor_bypass(
mock_entry: MockConfigEntry, hass: HomeAssistant
) -> None:
"""Test cell state sensor in bypass state."""
# Arrange
metrics = {"A_CYC_CELL_STATE": 2}
# Act
with patch_metrics(metrics=metrics):
await hass.config_entries.async_setup(mock_entry.entry_id)
await hass.async_block_till_done()
# Assert
sensor = hass.states.get("sensor.vallox_cell_state")
assert sensor.state == "Bypass"
async def test_cell_state_sensor_defrosting(
mock_entry: MockConfigEntry, hass: HomeAssistant
) -> None:
"""Test cell state sensor in defrosting state."""
# Arrange
metrics = {"A_CYC_CELL_STATE": 3}
# Act
with patch_metrics(metrics=metrics):
await hass.config_entries.async_setup(mock_entry.entry_id)
await hass.async_block_till_done()
# Assert
sensor = hass.states.get("sensor.vallox_cell_state")
assert sensor.state == "Defrosting"
async def test_cell_state_sensor_unknown_state(
mock_entry: MockConfigEntry, hass: HomeAssistant
) -> None:
"""Test cell state sensor in unknown state."""
# Arrange
metrics = {"A_CYC_CELL_STATE": 4}
# Act
with patch_metrics(metrics=metrics):
await hass.config_entries.async_setup(mock_entry.entry_id)
await hass.async_block_till_done()
# Assert
sensor = hass.states.get("sensor.vallox_cell_state")
assert sensor.state == "unknown"

View File

@ -1,5 +1,4 @@
"""Tests for Vallox switch platform.""" """Tests for Vallox switch platform."""
from unittest.mock import patch
import pytest import pytest
@ -7,7 +6,7 @@ from homeassistant.components.switch.const import DOMAIN as SWITCH_DOMAIN
from homeassistant.const import ATTR_ENTITY_ID, SERVICE_TURN_OFF, SERVICE_TURN_ON from homeassistant.const import ATTR_ENTITY_ID, SERVICE_TURN_OFF, SERVICE_TURN_ON
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from .conftest import patch_metrics, patch_metrics_set from .conftest import patch_set_values
from tests.common import MockConfigEntry from tests.common import MockConfigEntry
@ -26,15 +25,13 @@ async def test_switch_entities(
expected_state: str, expected_state: str,
mock_entry: MockConfigEntry, mock_entry: MockConfigEntry,
hass: HomeAssistant, hass: HomeAssistant,
setup_fetch_metric_data_mock,
) -> None: ) -> None:
"""Test switch entities.""" """Test switch entities."""
# Arrange # Arrange
metrics = {metric_key: value} setup_fetch_metric_data_mock(metrics={metric_key: value})
# Act # Act
with patch_metrics(metrics=metrics), patch(
"homeassistant.components.vallox.Vallox.set_settable_address"
):
await hass.config_entries.async_setup(mock_entry.entry_id) await hass.config_entries.async_setup(mock_entry.entry_id)
await hass.async_block_till_done() await hass.async_block_till_done()
@ -60,9 +57,7 @@ async def test_bypass_lock_switch_entitity_set(
) -> None: ) -> None:
"""Test bypass lock switch set.""" """Test bypass lock switch set."""
# Act # Act
with patch_metrics(metrics={}), patch_metrics_set() as metrics_set, patch( with patch_set_values() as set_values:
"homeassistant.components.vallox.Vallox.set_settable_address"
):
await hass.config_entries.async_setup(mock_entry.entry_id) await hass.config_entries.async_setup(mock_entry.entry_id)
await hass.async_block_till_done() await hass.async_block_till_done()
await hass.services.async_call( await hass.services.async_call(
@ -71,4 +66,4 @@ async def test_bypass_lock_switch_entitity_set(
service_data={ATTR_ENTITY_ID: "switch.vallox_bypass_locked"}, service_data={ATTR_ENTITY_ID: "switch.vallox_bypass_locked"},
) )
await hass.async_block_till_done() await hass.async_block_till_done()
metrics_set.assert_called_once_with({metric_key: value}) set_values.assert_called_once_with({metric_key: value})