-
-
Notifications
You must be signed in to change notification settings - Fork 32.7k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add Vallox temperature control entities (#75858)
Co-authored-by: Sebastian Lövdahl <[email protected]> Co-authored-by: Andre Richter <[email protected]> Co-authored-by: Martin Hjelmare <[email protected]>
- Loading branch information
1 parent
687d162
commit 1572221
Showing
3 changed files
with
208 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -63,6 +63,7 @@ | |
Platform.SENSOR, | ||
Platform.FAN, | ||
Platform.BINARY_SENSOR, | ||
Platform.NUMBER, | ||
Platform.SWITCH, | ||
] | ||
|
||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,127 @@ | ||
"""Support for Vallox ventilation unit numbers.""" | ||
from __future__ import annotations | ||
|
||
from dataclasses import dataclass | ||
|
||
from vallox_websocket_api import Vallox | ||
|
||
from homeassistant.components.number import ( | ||
NumberDeviceClass, | ||
NumberEntity, | ||
NumberEntityDescription, | ||
) | ||
from homeassistant.config_entries import ConfigEntry | ||
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 . import ValloxDataUpdateCoordinator, ValloxEntity | ||
from .const import DOMAIN | ||
|
||
|
||
class ValloxNumberEntity(ValloxEntity, NumberEntity): | ||
"""Representation of a Vallox number entity.""" | ||
|
||
entity_description: ValloxNumberEntityDescription | ||
_attr_has_entity_name = True | ||
_attr_entity_category = EntityCategory.CONFIG | ||
|
||
def __init__( | ||
self, | ||
name: str, | ||
coordinator: ValloxDataUpdateCoordinator, | ||
description: ValloxNumberEntityDescription, | ||
client: Vallox, | ||
) -> None: | ||
"""Initialize the Vallox number entity.""" | ||
super().__init__(name, coordinator) | ||
|
||
self.entity_description = description | ||
|
||
self._attr_unique_id = f"{self._device_uuid}-{description.key}" | ||
self._client = client | ||
|
||
@property | ||
def native_value(self) -> float | None: | ||
"""Return the value reported by the sensor.""" | ||
if ( | ||
value := self.coordinator.data.get_metric( | ||
self.entity_description.metric_key | ||
) | ||
) is None: | ||
return None | ||
|
||
return float(value) | ||
|
||
async def async_set_native_value(self, value: float) -> None: | ||
"""Update the current value.""" | ||
await self._client.set_values( | ||
{self.entity_description.metric_key: float(value)} | ||
) | ||
await self.coordinator.async_request_refresh() | ||
|
||
|
||
@dataclass | ||
class ValloxMetricMixin: | ||
"""Holds Vallox metric key.""" | ||
|
||
metric_key: str | ||
|
||
|
||
@dataclass | ||
class ValloxNumberEntityDescription(NumberEntityDescription, ValloxMetricMixin): | ||
"""Describes Vallox number entity.""" | ||
|
||
|
||
NUMBER_ENTITIES: tuple[ValloxNumberEntityDescription, ...] = ( | ||
ValloxNumberEntityDescription( | ||
key="supply_air_target_home", | ||
name="Supply air temperature (Home)", | ||
metric_key="A_CYC_HOME_AIR_TEMP_TARGET", | ||
device_class=NumberDeviceClass.TEMPERATURE, | ||
native_unit_of_measurement=TEMP_CELSIUS, | ||
icon="mdi:thermometer", | ||
native_min_value=5.0, | ||
native_max_value=25.0, | ||
native_step=1.0, | ||
), | ||
ValloxNumberEntityDescription( | ||
key="supply_air_target_away", | ||
name="Supply air temperature (Away)", | ||
metric_key="A_CYC_AWAY_AIR_TEMP_TARGET", | ||
device_class=NumberDeviceClass.TEMPERATURE, | ||
native_unit_of_measurement=TEMP_CELSIUS, | ||
icon="mdi:thermometer", | ||
native_min_value=5.0, | ||
native_max_value=25.0, | ||
native_step=1.0, | ||
), | ||
ValloxNumberEntityDescription( | ||
key="supply_air_target_boost", | ||
name="Supply air temperature (Boost)", | ||
metric_key="A_CYC_BOOST_AIR_TEMP_TARGET", | ||
device_class=NumberDeviceClass.TEMPERATURE, | ||
native_unit_of_measurement=TEMP_CELSIUS, | ||
icon="mdi:thermometer", | ||
native_min_value=5.0, | ||
native_max_value=25.0, | ||
native_step=1.0, | ||
), | ||
) | ||
|
||
|
||
async def async_setup_entry( | ||
hass: HomeAssistant, entry: ConfigEntry, async_add_entities: AddEntitiesCallback | ||
) -> None: | ||
"""Set up the sensors.""" | ||
data = hass.data[DOMAIN][entry.entry_id] | ||
|
||
async_add_entities( | ||
[ | ||
ValloxNumberEntity( | ||
data["name"], data["coordinator"], description, data["client"] | ||
) | ||
for description in NUMBER_ENTITIES | ||
] | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,80 @@ | ||
"""Tests for Vallox number platform.""" | ||
import pytest | ||
|
||
from homeassistant.components.number.const import ( | ||
ATTR_VALUE, | ||
DOMAIN as NUMBER_DOMAIN, | ||
SERVICE_SET_VALUE, | ||
) | ||
from homeassistant.const import ATTR_ENTITY_ID | ||
from homeassistant.core import HomeAssistant | ||
|
||
from .conftest import patch_metrics, patch_metrics_set | ||
|
||
from tests.common import MockConfigEntry | ||
|
||
TEST_TEMPERATURE_ENTITIES_DATA = [ | ||
( | ||
"number.vallox_supply_air_temperature_home", | ||
"A_CYC_HOME_AIR_TEMP_TARGET", | ||
19.0, | ||
), | ||
( | ||
"number.vallox_supply_air_temperature_away", | ||
"A_CYC_AWAY_AIR_TEMP_TARGET", | ||
18.0, | ||
), | ||
( | ||
"number.vallox_supply_air_temperature_boost", | ||
"A_CYC_BOOST_AIR_TEMP_TARGET", | ||
17.0, | ||
), | ||
] | ||
|
||
|
||
@pytest.mark.parametrize("entity_id, metric_key, value", TEST_TEMPERATURE_ENTITIES_DATA) | ||
async def test_temperature_number_entities( | ||
entity_id: str, | ||
metric_key: str, | ||
value: float, | ||
mock_entry: MockConfigEntry, | ||
hass: HomeAssistant, | ||
) -> None: | ||
"""Test temperature entities.""" | ||
# Arrange | ||
metrics = {metric_key: value} | ||
|
||
# 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(entity_id) | ||
assert sensor.state == str(value) | ||
assert sensor.attributes["unit_of_measurement"] == "°C" | ||
|
||
|
||
@pytest.mark.parametrize("entity_id, metric_key, value", TEST_TEMPERATURE_ENTITIES_DATA) | ||
async def test_temperature_number_entity_set( | ||
entity_id: str, | ||
metric_key: str, | ||
value: float, | ||
mock_entry: MockConfigEntry, | ||
hass: HomeAssistant, | ||
) -> None: | ||
"""Test temperature set.""" | ||
# Act | ||
with patch_metrics(metrics={}), patch_metrics_set() as metrics_set: | ||
await hass.config_entries.async_setup(mock_entry.entry_id) | ||
await hass.async_block_till_done() | ||
await hass.services.async_call( | ||
NUMBER_DOMAIN, | ||
SERVICE_SET_VALUE, | ||
service_data={ | ||
ATTR_ENTITY_ID: entity_id, | ||
ATTR_VALUE: value, | ||
}, | ||
) | ||
await hass.async_block_till_done() | ||
metrics_set.assert_called_once_with({metric_key: value}) |