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:
parent
64636a4310
commit
3a00c95113
6 changed files with 93 additions and 37 deletions
|
@ -5,14 +5,16 @@ from dataclasses import dataclass, field
|
||||||
from datetime import date
|
from datetime import date
|
||||||
import ipaddress
|
import ipaddress
|
||||||
import logging
|
import logging
|
||||||
from typing import Any, NamedTuple
|
from typing import Any, NamedTuple, cast
|
||||||
from uuid import UUID
|
from uuid import UUID
|
||||||
|
|
||||||
from vallox_websocket_api import PROFILE as VALLOX_PROFILE, Vallox
|
from vallox_websocket_api import PROFILE as VALLOX_PROFILE, Vallox
|
||||||
from vallox_websocket_api.exceptions import ValloxApiException
|
from vallox_websocket_api.exceptions import ValloxApiException
|
||||||
from vallox_websocket_api.vallox import (
|
from vallox_websocket_api.vallox import (
|
||||||
get_next_filter_change_date as calculate_next_filter_change_date,
|
get_model as _api_get_model,
|
||||||
get_uuid as calculate_uuid,
|
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
|
||||||
|
|
||||||
|
@ -20,8 +22,13 @@ from homeassistant.config_entries import SOURCE_IMPORT, ConfigEntry
|
||||||
from homeassistant.const import CONF_HOST, CONF_NAME, Platform
|
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.entity import DeviceInfo
|
||||||
from homeassistant.helpers.typing import ConfigType, StateType
|
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 (
|
from .const import (
|
||||||
DEFAULT_FAN_SPEED_AWAY,
|
DEFAULT_FAN_SPEED_AWAY,
|
||||||
|
@ -114,16 +121,32 @@ class ValloxState:
|
||||||
|
|
||||||
return value
|
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."""
|
"""Return cached UUID value."""
|
||||||
uuid = calculate_uuid(self.metric_cache)
|
uuid = _api_get_uuid(self.metric_cache)
|
||||||
if not isinstance(uuid, UUID):
|
if not isinstance(uuid, UUID):
|
||||||
raise ValueError
|
raise ValueError
|
||||||
return uuid
|
return uuid
|
||||||
|
|
||||||
def get_next_filter_change_date(self) -> date | None:
|
def get_next_filter_change_date(self) -> date | None:
|
||||||
"""Return the next filter change date."""
|
"""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):
|
if not isinstance(next_filter_change_date, date):
|
||||||
return None
|
return None
|
||||||
|
@ -291,3 +314,22 @@ class ValloxServiceHandler:
|
||||||
# be observed by all parties involved.
|
# be observed by all parties involved.
|
||||||
if result:
|
if result:
|
||||||
await self._coordinator.async_request_refresh()
|
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]}",
|
||||||
|
)
|
||||||
|
|
|
@ -9,19 +9,18 @@ from homeassistant.components.binary_sensor import (
|
||||||
)
|
)
|
||||||
from homeassistant.config_entries import ConfigEntry
|
from homeassistant.config_entries import ConfigEntry
|
||||||
from homeassistant.core import HomeAssistant
|
from homeassistant.core import HomeAssistant
|
||||||
|
from homeassistant.helpers.entity import EntityCategory
|
||||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
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
|
from .const import DOMAIN
|
||||||
|
|
||||||
|
|
||||||
class ValloxBinarySensor(
|
class ValloxBinarySensor(ValloxEntity, BinarySensorEntity):
|
||||||
CoordinatorEntity[ValloxDataUpdateCoordinator], BinarySensorEntity
|
|
||||||
):
|
|
||||||
"""Representation of a Vallox binary sensor."""
|
"""Representation of a Vallox binary sensor."""
|
||||||
|
|
||||||
entity_description: ValloxBinarySensorEntityDescription
|
entity_description: ValloxBinarySensorEntityDescription
|
||||||
|
_attr_entity_category = EntityCategory.DIAGNOSTIC
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
|
@ -30,14 +29,12 @@ class ValloxBinarySensor(
|
||||||
description: ValloxBinarySensorEntityDescription,
|
description: ValloxBinarySensorEntityDescription,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Initialize the Vallox binary sensor."""
|
"""Initialize the Vallox binary sensor."""
|
||||||
super().__init__(coordinator)
|
super().__init__(name, coordinator)
|
||||||
|
|
||||||
self.entity_description = description
|
self.entity_description = description
|
||||||
|
|
||||||
self._attr_name = f"{name} {description.name}"
|
self._attr_name = f"{name} {description.name}"
|
||||||
|
self._attr_unique_id = f"{self._device_uuid}-{description.key}"
|
||||||
uuid = self.coordinator.data.get_uuid()
|
|
||||||
self._attr_unique_id = f"{uuid}-{description.key}"
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def is_on(self) -> bool | None:
|
def is_on(self) -> bool | None:
|
||||||
|
|
|
@ -17,9 +17,8 @@ from homeassistant.config_entries import ConfigEntry
|
||||||
from homeassistant.core import HomeAssistant
|
from homeassistant.core import HomeAssistant
|
||||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||||
from homeassistant.helpers.typing import StateType
|
from homeassistant.helpers.typing import StateType
|
||||||
from homeassistant.helpers.update_coordinator import CoordinatorEntity
|
|
||||||
|
|
||||||
from . import ValloxDataUpdateCoordinator
|
from . import ValloxDataUpdateCoordinator, ValloxEntity
|
||||||
from .const import (
|
from .const import (
|
||||||
DOMAIN,
|
DOMAIN,
|
||||||
METRIC_KEY_MODE,
|
METRIC_KEY_MODE,
|
||||||
|
@ -80,7 +79,7 @@ async def async_setup_entry(
|
||||||
async_add_entities([device])
|
async_add_entities([device])
|
||||||
|
|
||||||
|
|
||||||
class ValloxFan(CoordinatorEntity[ValloxDataUpdateCoordinator], FanEntity):
|
class ValloxFan(ValloxEntity, FanEntity):
|
||||||
"""Representation of the fan."""
|
"""Representation of the fan."""
|
||||||
|
|
||||||
_attr_supported_features = FanEntityFeature.PRESET_MODE
|
_attr_supported_features = FanEntityFeature.PRESET_MODE
|
||||||
|
@ -92,13 +91,12 @@ class ValloxFan(CoordinatorEntity[ValloxDataUpdateCoordinator], FanEntity):
|
||||||
coordinator: ValloxDataUpdateCoordinator,
|
coordinator: ValloxDataUpdateCoordinator,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Initialize the fan."""
|
"""Initialize the fan."""
|
||||||
super().__init__(coordinator)
|
super().__init__(name, coordinator)
|
||||||
|
|
||||||
self._client = client
|
self._client = client
|
||||||
|
|
||||||
self._attr_name = name
|
self._attr_name = name
|
||||||
|
self._attr_unique_id = str(self._device_uuid)
|
||||||
self._attr_unique_id = str(self.coordinator.data.get_uuid())
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def preset_modes(self) -> list[str]:
|
def preset_modes(self) -> list[str]:
|
||||||
|
|
|
@ -17,12 +17,12 @@ from homeassistant.const import (
|
||||||
TEMP_CELSIUS,
|
TEMP_CELSIUS,
|
||||||
)
|
)
|
||||||
from homeassistant.core import HomeAssistant
|
from homeassistant.core import HomeAssistant
|
||||||
|
from homeassistant.helpers.entity import EntityCategory
|
||||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||||
from homeassistant.helpers.typing import StateType
|
from homeassistant.helpers.typing import StateType
|
||||||
from homeassistant.helpers.update_coordinator import CoordinatorEntity
|
|
||||||
from homeassistant.util import dt
|
from homeassistant.util import dt
|
||||||
|
|
||||||
from . import ValloxDataUpdateCoordinator
|
from . import ValloxDataUpdateCoordinator, ValloxEntity
|
||||||
from .const import (
|
from .const import (
|
||||||
DOMAIN,
|
DOMAIN,
|
||||||
METRIC_KEY_MODE,
|
METRIC_KEY_MODE,
|
||||||
|
@ -32,10 +32,11 @@ from .const import (
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class ValloxSensor(CoordinatorEntity[ValloxDataUpdateCoordinator], SensorEntity):
|
class ValloxSensor(ValloxEntity, SensorEntity):
|
||||||
"""Representation of a Vallox sensor."""
|
"""Representation of a Vallox sensor."""
|
||||||
|
|
||||||
entity_description: ValloxSensorEntityDescription
|
entity_description: ValloxSensorEntityDescription
|
||||||
|
_attr_entity_category = EntityCategory.DIAGNOSTIC
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
|
@ -44,14 +45,12 @@ class ValloxSensor(CoordinatorEntity[ValloxDataUpdateCoordinator], SensorEntity)
|
||||||
description: ValloxSensorEntityDescription,
|
description: ValloxSensorEntityDescription,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Initialize the Vallox sensor."""
|
"""Initialize the Vallox sensor."""
|
||||||
super().__init__(coordinator)
|
super().__init__(name, coordinator)
|
||||||
|
|
||||||
self.entity_description = description
|
self.entity_description = description
|
||||||
|
|
||||||
self._attr_name = f"{name} {description.name}"
|
self._attr_name = f"{name} {description.name}"
|
||||||
|
self._attr_unique_id = f"{self._device_uuid}-{description.key}"
|
||||||
uuid = self.coordinator.data.get_uuid()
|
|
||||||
self._attr_unique_id = f"{uuid}-{description.key}"
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def native_value(self) -> StateType | datetime:
|
def native_value(self) -> StateType | datetime:
|
||||||
|
|
|
@ -50,10 +50,30 @@ def patch_profile_home():
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(autouse=True)
|
@pytest.fixture(autouse=True)
|
||||||
def patch_uuid():
|
def patch_model():
|
||||||
"""Patch the Vallox entity UUID."""
|
"""Patch the Vallox model response."""
|
||||||
with patch(
|
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(),
|
return_value=_random_uuid(),
|
||||||
):
|
):
|
||||||
yield
|
yield
|
||||||
|
|
|
@ -51,7 +51,7 @@ async def test_remaining_filter_returns_timestamp(
|
||||||
"""Test that the remaining time for filter sensor returns a timestamp."""
|
"""Test that the remaining time for filter sensor returns a timestamp."""
|
||||||
# Act
|
# Act
|
||||||
with patch(
|
with patch(
|
||||||
"homeassistant.components.vallox.calculate_next_filter_change_date",
|
"homeassistant.components.vallox._api_get_next_filter_change_date",
|
||||||
return_value=dt.now().date(),
|
return_value=dt.now().date(),
|
||||||
), patch_metrics(metrics={}):
|
), patch_metrics(metrics={}):
|
||||||
await hass.config_entries.async_setup(mock_entry.entry_id)
|
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."""
|
"""Test that the remaining time for filter sensor returns 'unknown' when Vallox returns None."""
|
||||||
# Act
|
# Act
|
||||||
with patch(
|
with patch(
|
||||||
"homeassistant.components.vallox.calculate_next_filter_change_date",
|
"homeassistant.components.vallox._api_get_next_filter_change_date",
|
||||||
return_value=None,
|
return_value=None,
|
||||||
), patch_metrics(metrics={}):
|
), patch_metrics(metrics={}):
|
||||||
await hass.config_entries.async_setup(mock_entry.entry_id)
|
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
|
# Act
|
||||||
with patch(
|
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,
|
return_value=mocked_filter_end_date,
|
||||||
), patch_metrics(metrics={}):
|
), patch_metrics(metrics={}):
|
||||||
await hass.config_entries.async_setup(mock_entry.entry_id)
|
await hass.config_entries.async_setup(mock_entry.entry_id)
|
||||||
|
@ -122,7 +122,7 @@ async def test_remaining_time_for_filter_today(
|
||||||
|
|
||||||
# Act
|
# Act
|
||||||
with patch(
|
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,
|
return_value=mocked_filter_end_date,
|
||||||
), patch_metrics(metrics={}):
|
), patch_metrics(metrics={}):
|
||||||
await hass.config_entries.async_setup(mock_entry.entry_id)
|
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
|
# Act
|
||||||
with patch(
|
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,
|
return_value=mocked_filter_end_date,
|
||||||
), patch_metrics(metrics={}):
|
), patch_metrics(metrics={}):
|
||||||
await hass.config_entries.async_setup(mock_entry.entry_id)
|
await hass.config_entries.async_setup(mock_entry.entry_id)
|
||||||
|
|
Loading…
Add table
Reference in a new issue