From 3a00c95113eb86c0b58c04509a9ff32bc6688836 Mon Sep 17 00:00:00 2001 From: Andre Richter Date: Tue, 10 May 2022 00:00:31 +0200 Subject: [PATCH] Add device_info and entity_category to Vallox (#67353) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Add device_info and entity_category to Vallox * Fix DeviceInfo * Address review comments 1 * vallox suggested changes Co-authored-by: Sebastian Lövdahl Co-authored-by: J. Nick Koston --- homeassistant/components/vallox/__init__.py | 56 ++++++++++++++++--- .../components/vallox/binary_sensor.py | 15 ++--- homeassistant/components/vallox/fan.py | 10 ++-- homeassistant/components/vallox/sensor.py | 13 ++--- tests/components/vallox/conftest.py | 26 ++++++++- tests/components/vallox/test_sensor.py | 10 ++-- 6 files changed, 93 insertions(+), 37 deletions(-) diff --git a/homeassistant/components/vallox/__init__.py b/homeassistant/components/vallox/__init__.py index 23e5cb53f97..a69542596c8 100644 --- a/homeassistant/components/vallox/__init__.py +++ b/homeassistant/components/vallox/__init__.py @@ -5,14 +5,16 @@ from dataclasses import dataclass, field from datetime import date import ipaddress import logging -from typing import Any, NamedTuple +from typing import Any, NamedTuple, cast from uuid import UUID from vallox_websocket_api import PROFILE as VALLOX_PROFILE, Vallox from vallox_websocket_api.exceptions import ValloxApiException from vallox_websocket_api.vallox import ( - get_next_filter_change_date as calculate_next_filter_change_date, - get_uuid as calculate_uuid, + 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 @@ -20,8 +22,13 @@ from homeassistant.config_entries import SOURCE_IMPORT, ConfigEntry from homeassistant.const import CONF_HOST, CONF_NAME, Platform from homeassistant.core import HomeAssistant, ServiceCall from homeassistant.helpers import config_validation as cv +from homeassistant.helpers.entity import DeviceInfo from homeassistant.helpers.typing import ConfigType, StateType -from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed +from homeassistant.helpers.update_coordinator import ( + CoordinatorEntity, + DataUpdateCoordinator, + UpdateFailed, +) from .const import ( DEFAULT_FAN_SPEED_AWAY, @@ -114,16 +121,32 @@ class ValloxState: return value - def get_uuid(self) -> UUID | None: + @property + def model(self) -> str | None: + """Return the model, if any.""" + model = cast(str, _api_get_model(self.metric_cache)) + + if model == "Unknown": + return None + + return model + + @property + def sw_version(self) -> str: + """Return the SW version.""" + return cast(str, _api_get_sw_version(self.metric_cache)) + + @property + def uuid(self) -> UUID | None: """Return cached UUID value.""" - uuid = calculate_uuid(self.metric_cache) + uuid = _api_get_uuid(self.metric_cache) if not isinstance(uuid, UUID): raise ValueError return uuid def get_next_filter_change_date(self) -> date | None: """Return the next filter change date.""" - next_filter_change_date = calculate_next_filter_change_date(self.metric_cache) + next_filter_change_date = _api_get_next_filter_change_date(self.metric_cache) if not isinstance(next_filter_change_date, date): return None @@ -291,3 +314,22 @@ class ValloxServiceHandler: # be observed by all parties involved. if result: await self._coordinator.async_request_refresh() + + +class ValloxEntity(CoordinatorEntity[ValloxDataUpdateCoordinator]): + """Representation of a Vallox entity.""" + + def __init__(self, name: str, coordinator: ValloxDataUpdateCoordinator) -> None: + """Initialize a Vallox entity.""" + super().__init__(coordinator) + + self._device_uuid = self.coordinator.data.uuid + assert self.coordinator.config_entry is not None + self._attr_device_info = DeviceInfo( + identifiers={(DOMAIN, str(self._device_uuid))}, + manufacturer=DEFAULT_NAME, + model=self.coordinator.data.model, + name=name, + sw_version=self.coordinator.data.sw_version, + configuration_url=f"http://{self.coordinator.config_entry.data[CONF_HOST]}", + ) diff --git a/homeassistant/components/vallox/binary_sensor.py b/homeassistant/components/vallox/binary_sensor.py index 348bad97158..762b63c0c1d 100644 --- a/homeassistant/components/vallox/binary_sensor.py +++ b/homeassistant/components/vallox/binary_sensor.py @@ -9,19 +9,18 @@ from homeassistant.components.binary_sensor import ( ) from homeassistant.config_entries import ConfigEntry from homeassistant.core import HomeAssistant +from homeassistant.helpers.entity import EntityCategory from homeassistant.helpers.entity_platform import AddEntitiesCallback -from homeassistant.helpers.update_coordinator import CoordinatorEntity -from . import ValloxDataUpdateCoordinator +from . import ValloxDataUpdateCoordinator, ValloxEntity from .const import DOMAIN -class ValloxBinarySensor( - CoordinatorEntity[ValloxDataUpdateCoordinator], BinarySensorEntity -): +class ValloxBinarySensor(ValloxEntity, BinarySensorEntity): """Representation of a Vallox binary sensor.""" entity_description: ValloxBinarySensorEntityDescription + _attr_entity_category = EntityCategory.DIAGNOSTIC def __init__( self, @@ -30,14 +29,12 @@ class ValloxBinarySensor( description: ValloxBinarySensorEntityDescription, ) -> None: """Initialize the Vallox binary sensor.""" - super().__init__(coordinator) + super().__init__(name, coordinator) self.entity_description = description self._attr_name = f"{name} {description.name}" - - uuid = self.coordinator.data.get_uuid() - self._attr_unique_id = f"{uuid}-{description.key}" + self._attr_unique_id = f"{self._device_uuid}-{description.key}" @property def is_on(self) -> bool | None: diff --git a/homeassistant/components/vallox/fan.py b/homeassistant/components/vallox/fan.py index 85b3f501c85..6872acbb5b7 100644 --- a/homeassistant/components/vallox/fan.py +++ b/homeassistant/components/vallox/fan.py @@ -17,9 +17,8 @@ from homeassistant.config_entries import ConfigEntry from homeassistant.core import HomeAssistant from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.typing import StateType -from homeassistant.helpers.update_coordinator import CoordinatorEntity -from . import ValloxDataUpdateCoordinator +from . import ValloxDataUpdateCoordinator, ValloxEntity from .const import ( DOMAIN, METRIC_KEY_MODE, @@ -80,7 +79,7 @@ async def async_setup_entry( async_add_entities([device]) -class ValloxFan(CoordinatorEntity[ValloxDataUpdateCoordinator], FanEntity): +class ValloxFan(ValloxEntity, FanEntity): """Representation of the fan.""" _attr_supported_features = FanEntityFeature.PRESET_MODE @@ -92,13 +91,12 @@ class ValloxFan(CoordinatorEntity[ValloxDataUpdateCoordinator], FanEntity): coordinator: ValloxDataUpdateCoordinator, ) -> None: """Initialize the fan.""" - super().__init__(coordinator) + super().__init__(name, coordinator) self._client = client self._attr_name = name - - self._attr_unique_id = str(self.coordinator.data.get_uuid()) + self._attr_unique_id = str(self._device_uuid) @property def preset_modes(self) -> list[str]: diff --git a/homeassistant/components/vallox/sensor.py b/homeassistant/components/vallox/sensor.py index 92f0bc32e76..54a010c3e3d 100644 --- a/homeassistant/components/vallox/sensor.py +++ b/homeassistant/components/vallox/sensor.py @@ -17,12 +17,12 @@ from homeassistant.const import ( TEMP_CELSIUS, ) from homeassistant.core import HomeAssistant +from homeassistant.helpers.entity import EntityCategory from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.typing import StateType -from homeassistant.helpers.update_coordinator import CoordinatorEntity from homeassistant.util import dt -from . import ValloxDataUpdateCoordinator +from . import ValloxDataUpdateCoordinator, ValloxEntity from .const import ( DOMAIN, METRIC_KEY_MODE, @@ -32,10 +32,11 @@ from .const import ( ) -class ValloxSensor(CoordinatorEntity[ValloxDataUpdateCoordinator], SensorEntity): +class ValloxSensor(ValloxEntity, SensorEntity): """Representation of a Vallox sensor.""" entity_description: ValloxSensorEntityDescription + _attr_entity_category = EntityCategory.DIAGNOSTIC def __init__( self, @@ -44,14 +45,12 @@ class ValloxSensor(CoordinatorEntity[ValloxDataUpdateCoordinator], SensorEntity) description: ValloxSensorEntityDescription, ) -> None: """Initialize the Vallox sensor.""" - super().__init__(coordinator) + super().__init__(name, coordinator) self.entity_description = description self._attr_name = f"{name} {description.name}" - - uuid = self.coordinator.data.get_uuid() - self._attr_unique_id = f"{uuid}-{description.key}" + self._attr_unique_id = f"{self._device_uuid}-{description.key}" @property def native_value(self) -> StateType | datetime: diff --git a/tests/components/vallox/conftest.py b/tests/components/vallox/conftest.py index e7ea6ee6d6e..ef9fd2a0e4b 100644 --- a/tests/components/vallox/conftest.py +++ b/tests/components/vallox/conftest.py @@ -50,10 +50,30 @@ def patch_profile_home(): @pytest.fixture(autouse=True) -def patch_uuid(): - """Patch the Vallox entity UUID.""" +def patch_model(): + """Patch the Vallox model response.""" with patch( - "homeassistant.components.vallox.calculate_uuid", + "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 diff --git a/tests/components/vallox/test_sensor.py b/tests/components/vallox/test_sensor.py index dea3f79fbd8..9c96f6f0a68 100644 --- a/tests/components/vallox/test_sensor.py +++ b/tests/components/vallox/test_sensor.py @@ -51,7 +51,7 @@ async def test_remaining_filter_returns_timestamp( """Test that the remaining time for filter sensor returns a timestamp.""" # Act with patch( - "homeassistant.components.vallox.calculate_next_filter_change_date", + "homeassistant.components.vallox._api_get_next_filter_change_date", return_value=dt.now().date(), ), patch_metrics(metrics={}): await hass.config_entries.async_setup(mock_entry.entry_id) @@ -68,7 +68,7 @@ async def test_remaining_time_for_filter_none_returned_from_vallox( """Test that the remaining time for filter sensor returns 'unknown' when Vallox returns None.""" # Act with patch( - "homeassistant.components.vallox.calculate_next_filter_change_date", + "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) @@ -98,7 +98,7 @@ async def test_remaining_time_for_filter_in_the_future( # Act with patch( - "homeassistant.components.vallox.calculate_next_filter_change_date", + "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) @@ -122,7 +122,7 @@ async def test_remaining_time_for_filter_today( # Act with patch( - "homeassistant.components.vallox.calculate_next_filter_change_date", + "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) @@ -146,7 +146,7 @@ async def test_remaining_time_for_filter_in_the_past( # Act with patch( - "homeassistant.components.vallox.calculate_next_filter_change_date", + "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)