Add device_info and entity_category to Vallox (#67353)

* Add device_info and entity_category to Vallox

* Fix DeviceInfo

* Address review comments 1

* vallox suggested changes

Co-authored-by: Sebastian Lövdahl <slovdahl@hibox.fi>
Co-authored-by: J. Nick Koston <nick@koston.org>
This commit is contained in:
Andre Richter 2022-05-10 00:00:31 +02:00 committed by GitHub
parent 64636a4310
commit 3a00c95113
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 93 additions and 37 deletions

View file

@ -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]}",
)

View file

@ -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:

View file

@ -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]:

View file

@ -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:

View file

@ -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

View file

@ -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)