Add Fan Speed number entity to Nexia (#98642)

* Add Fan Speed support to Nexia integration

Adds a new "set_fan_speed" service to the Nexia integration, to allow
setting speed of the air-handler/blower fans.

* Add Fan Speed to Nexia Tests

* Remove mistakenly-added changes to Climate tests

A previous version of this change made modifications to the base
Climate entity, but that approach was later abandonded. These changes
to Climate tests were left over from that, and should never have been
included.

* Add Fan Speed Number Entity

* Remove Set Fan Speed Service

* Remove fan_speed attribute

The fan_speed attribute is not necessary with the new Number entity.

* Address reviewer feedback

Rename test function to reflect fact that fan speed entities are
entities, and not sensors.
Added missing typing to variables.
Sorted list of platforms

* Add test_set_fan_speed

Also adds new test fixture for mock response to API call

* Update homeassistant/components/nexia/number.py

* Name change

---------

Co-authored-by: G Johansson <goran.johansson@shiftit.se>
This commit is contained in:
Benjamin Paul [MSFT] 2023-08-19 10:42:13 -04:00 committed by GitHub
parent 3094991236
commit 5965918c86
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 3227 additions and 1 deletions

View File

@ -2,10 +2,11 @@
from homeassistant.const import Platform
PLATFORMS = [
Platform.SENSOR,
Platform.BINARY_SENSOR,
Platform.CLIMATE,
Platform.NUMBER,
Platform.SCENE,
Platform.SENSOR,
Platform.SWITCH,
]

View File

@ -0,0 +1,72 @@
"""Support for Nexia / Trane XL Thermostats."""
from __future__ import annotations
from nexia.home import NexiaHome
from nexia.thermostat import NexiaThermostat
from homeassistant.components.number import NumberEntity
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import PERCENTAGE
from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from .const import DOMAIN
from .coordinator import NexiaDataUpdateCoordinator
from .entity import NexiaThermostatEntity
from .util import percent_conv
async def async_setup_entry(
hass: HomeAssistant,
config_entry: ConfigEntry,
async_add_entities: AddEntitiesCallback,
) -> None:
"""Set up sensors for a Nexia device."""
coordinator: NexiaDataUpdateCoordinator = hass.data[DOMAIN][config_entry.entry_id]
nexia_home: NexiaHome = coordinator.nexia_home
entities: list[NexiaThermostatEntity] = []
for thermostat_id in nexia_home.get_thermostat_ids():
thermostat = nexia_home.get_thermostat_by_id(thermostat_id)
if thermostat.has_variable_fan_speed():
entities.append(
NexiaFanSpeedEntity(
coordinator, thermostat, thermostat.get_variable_fan_speed_limits()
)
)
async_add_entities(entities)
class NexiaFanSpeedEntity(NexiaThermostatEntity, NumberEntity):
"""Provides Nexia Fan Speed support."""
_attr_native_unit_of_measurement = PERCENTAGE
_attr_icon = "mdi:fan"
def __init__(
self,
coordinator: NexiaDataUpdateCoordinator,
thermostat: NexiaThermostat,
valid_range: tuple[float, float],
) -> None:
"""Initialize the entity."""
super().__init__(
coordinator,
thermostat,
name=f"{thermostat.get_name()} Fan speed",
unique_id=f"{thermostat.thermostat_id}_fan_speed_setpoint",
)
min_value, max_value = valid_range
self._attr_native_min_value = percent_conv(min_value)
self._attr_native_max_value = percent_conv(max_value)
@property
def native_value(self) -> float:
"""Return the current value."""
fan_speed = self._thermostat.get_fan_speed_setpoint()
return percent_conv(fan_speed)
async def async_set_native_value(self, value: float) -> None:
"""Set a new value."""
await self._thermostat.set_fan_setpoint(value / 100)
self._signal_thermostat_update()

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,62 @@
"""The number entity tests for the nexia platform."""
from homeassistant.components.number import (
ATTR_VALUE,
DOMAIN as NUMBER_DOMAIN,
SERVICE_SET_VALUE,
)
from homeassistant.core import HomeAssistant
from .util import async_init_integration
async def test_create_fan_speed_number_entities(hass: HomeAssistant) -> None:
"""Test creation of fan speed number entities."""
await async_init_integration(hass)
state = hass.states.get("number.master_suite_fan_speed")
assert state.state == "35.0"
expected_attributes = {
"attribution": "Data provided by Trane Technologies",
"friendly_name": "Master Suite Fan speed",
"min": 35,
"max": 100,
}
# Only test for a subset of attributes in case
# HA changes the implementation and a new one appears
assert all(
state.attributes[key] == expected_attributes[key] for key in expected_attributes
)
state = hass.states.get("number.downstairs_east_wing_fan_speed")
assert state.state == "35.0"
expected_attributes = {
"attribution": "Data provided by Trane Technologies",
"friendly_name": "Downstairs East Wing Fan speed",
"min": 35,
"max": 100,
}
# Only test for a subset of attributes in case
# HA changes the implementation and a new one appears
assert all(
state.attributes[key] == expected_attributes[key] for key in expected_attributes
)
async def test_set_fan_speed(hass: HomeAssistant) -> None:
"""Test setting fan speed."""
await async_init_integration(hass)
state_before = hass.states.get("number.master_suite_fan_speed")
assert state_before.state == "35.0"
await hass.services.async_call(
NUMBER_DOMAIN,
SERVICE_SET_VALUE,
service_data={ATTR_VALUE: 50},
blocking=True,
target={"entity_id": "number.master_suite_fan_speed"},
)
state = hass.states.get("number.master_suite_fan_speed")
assert state.state == "50.0"

View File

@ -22,6 +22,7 @@ async def async_init_integration(
house_fixture = "nexia/mobile_houses_123456.json"
session_fixture = "nexia/session_123456.json"
sign_in_fixture = "nexia/sign_in.json"
set_fan_speed_fixture = "nexia/set_fan_speed_2293892.json"
with mock_aiohttp_client() as mock_session, patch(
"nexia.home.load_or_create_uuid", return_value=uuid.uuid4()
):
@ -46,6 +47,10 @@ async def async_init_integration(
nexia.API_MOBILE_ACCOUNTS_SIGN_IN_URL,
text=load_fixture(sign_in_fixture),
)
mock_session.post(
"https://www.mynexia.com/mobile/xxl_thermostats/2293892/fan_speed",
text=load_fixture(set_fan_speed_fixture),
)
entry = MockConfigEntry(
domain=DOMAIN, data={CONF_USERNAME: "mock", CONF_PASSWORD: "mock"}
)