From 329595bf73af5c4bacaf0581ca0ad58f689149f3 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Tue, 7 Jun 2022 14:26:06 -1000 Subject: [PATCH] Make radiotherm hold mode a switch (#73104) --- .coveragerc | 3 + .../components/radiotherm/__init__.py | 48 +++++++--- .../components/radiotherm/climate.py | 92 +++---------------- .../components/radiotherm/config_flow.py | 38 +------- homeassistant/components/radiotherm/const.py | 2 - .../components/radiotherm/coordinator.py | 15 +-- homeassistant/components/radiotherm/entity.py | 44 +++++++++ homeassistant/components/radiotherm/switch.py | 65 +++++++++++++ homeassistant/components/radiotherm/util.py | 24 +++++ .../components/radiotherm/test_config_flow.py | 45 +-------- 10 files changed, 195 insertions(+), 181 deletions(-) create mode 100644 homeassistant/components/radiotherm/entity.py create mode 100644 homeassistant/components/radiotherm/switch.py create mode 100644 homeassistant/components/radiotherm/util.py diff --git a/.coveragerc b/.coveragerc index 5fa747267a5..3db90953f74 100644 --- a/.coveragerc +++ b/.coveragerc @@ -963,9 +963,12 @@ omit = homeassistant/components/radio_browser/__init__.py homeassistant/components/radio_browser/media_source.py homeassistant/components/radiotherm/__init__.py + homeassistant/components/radiotherm/entity.py homeassistant/components/radiotherm/climate.py homeassistant/components/radiotherm/coordinator.py homeassistant/components/radiotherm/data.py + homeassistant/components/radiotherm/switch.py + homeassistant/components/radiotherm/util.py homeassistant/components/rainbird/* homeassistant/components/raincloud/* homeassistant/components/rainmachine/__init__.py diff --git a/homeassistant/components/radiotherm/__init__.py b/homeassistant/components/radiotherm/__init__.py index 9e389af6719..091d2bb8005 100644 --- a/homeassistant/components/radiotherm/__init__.py +++ b/homeassistant/components/radiotherm/__init__.py @@ -1,7 +1,9 @@ """The radiotherm component.""" from __future__ import annotations +from collections.abc import Coroutine from socket import timeout +from typing import Any, TypeVar from radiotherm.validate import RadiothermTstatError @@ -10,30 +12,46 @@ from homeassistant.const import CONF_HOST, Platform from homeassistant.core import HomeAssistant from homeassistant.exceptions import ConfigEntryNotReady -from .const import CONF_HOLD_TEMP, DOMAIN +from .const import DOMAIN from .coordinator import RadioThermUpdateCoordinator from .data import async_get_init_data +from .util import async_set_time -PLATFORMS: list[Platform] = [Platform.CLIMATE] +PLATFORMS: list[Platform] = [Platform.CLIMATE, Platform.SWITCH] + +_T = TypeVar("_T") + + +async def _async_call_or_raise_not_ready( + coro: Coroutine[Any, Any, _T], host: str +) -> _T: + """Call a coro or raise ConfigEntryNotReady.""" + try: + return await coro + except RadiothermTstatError as ex: + msg = f"{host} was busy (invalid value returned): {ex}" + raise ConfigEntryNotReady(msg) from ex + except timeout as ex: + msg = f"{host} timed out waiting for a response: {ex}" + raise ConfigEntryNotReady(msg) from ex async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: """Set up Radio Thermostat from a config entry.""" host = entry.data[CONF_HOST] - try: - init_data = await async_get_init_data(hass, host) - except RadiothermTstatError as ex: - raise ConfigEntryNotReady( - f"{host} was busy (invalid value returned): {ex}" - ) from ex - except timeout as ex: - raise ConfigEntryNotReady( - f"{host} timed out waiting for a response: {ex}" - ) from ex - - hold_temp = entry.options[CONF_HOLD_TEMP] - coordinator = RadioThermUpdateCoordinator(hass, init_data, hold_temp) + init_coro = async_get_init_data(hass, host) + init_data = await _async_call_or_raise_not_ready(init_coro, host) + coordinator = RadioThermUpdateCoordinator(hass, init_data) await coordinator.async_config_entry_first_refresh() + + # Only set the time if the thermostat is + # not in hold mode since setting the time + # clears the hold for some strange design + # choice + if not coordinator.data.tstat["hold"]: + time_coro = async_set_time(hass, init_data.tstat) + await _async_call_or_raise_not_ready(time_coro, host) + hass.data.setdefault(DOMAIN, {})[entry.entry_id] = coordinator hass.config_entries.async_setup_platforms(entry, PLATFORMS) entry.async_on_unload(entry.add_update_listener(_async_update_listener)) diff --git a/homeassistant/components/radiotherm/climate.py b/homeassistant/components/radiotherm/climate.py index 63ee93c9c84..3f8e87e74a4 100644 --- a/homeassistant/components/radiotherm/climate.py +++ b/homeassistant/components/radiotherm/climate.py @@ -1,7 +1,6 @@ """Support for Radio Thermostat wifi-enabled home thermostats.""" from __future__ import annotations -from functools import partial import logging from typing import Any @@ -27,19 +26,13 @@ from homeassistant.const import ( TEMP_FAHRENHEIT, ) from homeassistant.core import HomeAssistant, callback -from homeassistant.exceptions import HomeAssistantError -from homeassistant.helpers import device_registry as dr import homeassistant.helpers.config_validation as cv -from homeassistant.helpers.entity import DeviceInfo from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType -from homeassistant.helpers.update_coordinator import CoordinatorEntity -from homeassistant.util import dt as dt_util from . import DOMAIN -from .const import CONF_HOLD_TEMP from .coordinator import RadioThermUpdateCoordinator -from .data import RadioThermUpdate +from .entity import RadioThermostatEntity _LOGGER = logging.getLogger(__name__) @@ -92,10 +85,11 @@ PRESET_MODE_TO_CODE = { CODE_TO_PRESET_MODE = {v: k for k, v in PRESET_MODE_TO_CODE.items()} -CODE_TO_HOLD_STATE = {0: False, 1: True} PARALLEL_UPDATES = 1 +CONF_HOLD_TEMP = "hold_temp" + def round_temp(temperature): """Round a temperature to the resolution of the thermostat. @@ -150,18 +144,17 @@ async def async_setup_platform( _LOGGER.error("No Radiotherm Thermostats detected") return - hold_temp: bool = config[CONF_HOLD_TEMP] for host in hosts: hass.async_create_task( hass.config_entries.flow.async_init( DOMAIN, context={"source": SOURCE_IMPORT}, - data={CONF_HOST: host, CONF_HOLD_TEMP: hold_temp}, + data={CONF_HOST: host}, ) ) -class RadioThermostat(CoordinatorEntity[RadioThermUpdateCoordinator], ClimateEntity): +class RadioThermostat(RadioThermostatEntity, ClimateEntity): """Representation of a Radio Thermostat.""" _attr_hvac_modes = OPERATION_LIST @@ -171,49 +164,22 @@ class RadioThermostat(CoordinatorEntity[RadioThermUpdateCoordinator], ClimateEnt def __init__(self, coordinator: RadioThermUpdateCoordinator) -> None: """Initialize the thermostat.""" super().__init__(coordinator) - self.device = coordinator.init_data.tstat - self._attr_name = coordinator.init_data.name - self._hold_temp = coordinator.hold_temp - self._hold_set = False - self._attr_unique_id = coordinator.init_data.mac - self._attr_device_info = DeviceInfo( - name=coordinator.init_data.name, - model=coordinator.init_data.model, - manufacturer="Radio Thermostats", - sw_version=coordinator.init_data.fw_version, - connections={(dr.CONNECTION_NETWORK_MAC, coordinator.init_data.mac)}, - ) - self._is_model_ct80 = isinstance(self.device, radiotherm.thermostat.CT80) + self._attr_name = self.init_data.name + self._attr_unique_id = self.init_data.mac + self._attr_fan_modes = CT30_FAN_OPERATION_LIST self._attr_supported_features = ( ClimateEntityFeature.TARGET_TEMPERATURE | ClimateEntityFeature.FAN_MODE ) - self._process_data() - if not self._is_model_ct80: - self._attr_fan_modes = CT30_FAN_OPERATION_LIST + if not isinstance(self.device, radiotherm.thermostat.CT80): return self._attr_fan_modes = CT80_FAN_OPERATION_LIST self._attr_supported_features |= ClimateEntityFeature.PRESET_MODE self._attr_preset_modes = PRESET_MODES - @property - def data(self) -> RadioThermUpdate: - """Returnt the last update.""" - return self.coordinator.data - - async def async_added_to_hass(self) -> None: - """Register callbacks.""" - # Set the time on the device. This shouldn't be in the - # constructor because it's a network call. We can't put it in - # update() because calling it will clear any temporary mode or - # temperature in the thermostat. So add it as a future job - # for the event loop to run. - self.hass.async_add_job(self.set_time) - await super().async_added_to_hass() - async def async_set_fan_mode(self, fan_mode: str) -> None: """Turn fan on/off.""" if (code := FAN_MODE_TO_CODE.get(fan_mode)) is None: - raise HomeAssistantError(f"{fan_mode} is not a valid fan mode") + raise ValueError(f"{fan_mode} is not a valid fan mode") await self.hass.async_add_executor_job(self._set_fan_mode, code) self._attr_fan_mode = fan_mode self.async_write_ha_state() @@ -223,16 +189,11 @@ class RadioThermostat(CoordinatorEntity[RadioThermUpdateCoordinator], ClimateEnt """Turn fan on/off.""" self.device.fmode = code - @callback - def _handle_coordinator_update(self) -> None: - self._process_data() - return super()._handle_coordinator_update() - @callback def _process_data(self) -> None: """Update and validate the data from the thermostat.""" data = self.data.tstat - if self._is_model_ct80: + if isinstance(self.device, radiotherm.thermostat.CT80): self._attr_current_humidity = self.data.humidity self._attr_preset_mode = CODE_TO_PRESET_MODE[data["program_mode"]] # Map thermostat values into various STATE_ flags. @@ -242,7 +203,6 @@ class RadioThermostat(CoordinatorEntity[RadioThermUpdateCoordinator], ClimateEnt ATTR_FAN_ACTION: CODE_TO_FAN_STATE[data["fstate"]] } self._attr_hvac_mode = CODE_TO_TEMP_MODE[data["tmode"]] - self._hold_set = CODE_TO_HOLD_STATE[data["hold"]] if self.hvac_mode == HVACMode.OFF: self._attr_hvac_action = None else: @@ -264,15 +224,12 @@ class RadioThermostat(CoordinatorEntity[RadioThermUpdateCoordinator], ClimateEnt """Set new target temperature.""" if (temperature := kwargs.get(ATTR_TEMPERATURE)) is None: return - hold_changed = kwargs.get("hold_changed", False) - await self.hass.async_add_executor_job( - partial(self._set_temperature, temperature, hold_changed) - ) + await self.hass.async_add_executor_job(self._set_temperature, temperature) self._attr_target_temperature = temperature self.async_write_ha_state() await self.coordinator.async_request_refresh() - def _set_temperature(self, temperature: int, hold_changed: bool) -> None: + def _set_temperature(self, temperature: int) -> None: """Set new target temperature.""" temperature = round_temp(temperature) if self.hvac_mode == HVACMode.COOL: @@ -285,26 +242,6 @@ class RadioThermostat(CoordinatorEntity[RadioThermUpdateCoordinator], ClimateEnt elif self.hvac_action == HVACAction.HEATING: self.device.t_heat = temperature - # Only change the hold if requested or if hold mode was turned - # on and we haven't set it yet. - if hold_changed or not self._hold_set: - if self._hold_temp: - self.device.hold = 1 - self._hold_set = True - else: - self.device.hold = 0 - - def set_time(self) -> None: - """Set device time.""" - # Calling this clears any local temperature override and - # reverts to the scheduled temperature. - now = dt_util.now() - self.device.time = { - "day": now.weekday(), - "hour": now.hour, - "minute": now.minute, - } - async def async_set_hvac_mode(self, hvac_mode: HVACMode) -> None: """Set operation mode (auto, cool, heat, off).""" await self.hass.async_add_executor_job(self._set_hvac_mode, hvac_mode) @@ -325,7 +262,7 @@ class RadioThermostat(CoordinatorEntity[RadioThermUpdateCoordinator], ClimateEnt async def async_set_preset_mode(self, preset_mode: str) -> None: """Set Preset mode (Home, Alternate, Away, Holiday).""" if preset_mode not in PRESET_MODES: - raise HomeAssistantError("{preset_mode} is not a valid preset_mode") + raise ValueError(f"{preset_mode} is not a valid preset_mode") await self.hass.async_add_executor_job(self._set_preset_mode, preset_mode) self._attr_preset_mode = preset_mode self.async_write_ha_state() @@ -333,4 +270,5 @@ class RadioThermostat(CoordinatorEntity[RadioThermUpdateCoordinator], ClimateEnt def _set_preset_mode(self, preset_mode: str) -> None: """Set Preset mode (Home, Alternate, Away, Holiday).""" + assert isinstance(self.device, radiotherm.thermostat.CT80) self.device.program_mode = PRESET_MODE_TO_CODE[preset_mode] diff --git a/homeassistant/components/radiotherm/config_flow.py b/homeassistant/components/radiotherm/config_flow.py index 45fee0f7fd9..030a3e6c022 100644 --- a/homeassistant/components/radiotherm/config_flow.py +++ b/homeassistant/components/radiotherm/config_flow.py @@ -11,11 +11,11 @@ import voluptuous as vol from homeassistant import config_entries from homeassistant.components import dhcp from homeassistant.const import CONF_HOST -from homeassistant.core import HomeAssistant, callback +from homeassistant.core import HomeAssistant from homeassistant.data_entry_flow import FlowResult from homeassistant.exceptions import HomeAssistantError -from .const import CONF_HOLD_TEMP, DOMAIN +from .const import DOMAIN from .data import RadioThermInitData, async_get_init_data _LOGGER = logging.getLogger(__name__) @@ -68,7 +68,6 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): return self.async_create_entry( title=init_data.name, data={CONF_HOST: ip_address}, - options={CONF_HOLD_TEMP: False}, ) self._set_confirm_only() @@ -100,7 +99,6 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): return self.async_create_entry( title=init_data.name, data={CONF_HOST: import_info[CONF_HOST]}, - options={CONF_HOLD_TEMP: import_info[CONF_HOLD_TEMP]}, ) async def async_step_user( @@ -125,7 +123,6 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): return self.async_create_entry( title=init_data.name, data=user_input, - options={CONF_HOLD_TEMP: False}, ) return self.async_show_form( @@ -133,34 +130,3 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): data_schema=vol.Schema({vol.Required(CONF_HOST): str}), errors=errors, ) - - @staticmethod - @callback - def async_get_options_flow(config_entry): - """Get the options flow for this handler.""" - return OptionsFlowHandler(config_entry) - - -class OptionsFlowHandler(config_entries.OptionsFlow): - """Handle a option flow for radiotherm.""" - - def __init__(self, config_entry: config_entries.ConfigEntry) -> None: - """Initialize options flow.""" - self.config_entry = config_entry - - async def async_step_init(self, user_input=None): - """Handle options flow.""" - if user_input is not None: - return self.async_create_entry(title="", data=user_input) - - return self.async_show_form( - step_id="init", - data_schema=vol.Schema( - { - vol.Optional( - CONF_HOLD_TEMP, - default=self.config_entry.options[CONF_HOLD_TEMP], - ): bool - } - ), - ) diff --git a/homeassistant/components/radiotherm/const.py b/homeassistant/components/radiotherm/const.py index 398747c6571..db097d40665 100644 --- a/homeassistant/components/radiotherm/const.py +++ b/homeassistant/components/radiotherm/const.py @@ -2,6 +2,4 @@ DOMAIN = "radiotherm" -CONF_HOLD_TEMP = "hold_temp" - TIMEOUT = 25 diff --git a/homeassistant/components/radiotherm/coordinator.py b/homeassistant/components/radiotherm/coordinator.py index 264a4a8d1fd..4afa2c0662b 100644 --- a/homeassistant/components/radiotherm/coordinator.py +++ b/homeassistant/components/radiotherm/coordinator.py @@ -20,12 +20,9 @@ UPDATE_INTERVAL = timedelta(seconds=15) class RadioThermUpdateCoordinator(DataUpdateCoordinator[RadioThermUpdate]): """DataUpdateCoordinator to gather data for radio thermostats.""" - def __init__( - self, hass: HomeAssistant, init_data: RadioThermInitData, hold_temp: bool - ) -> None: + def __init__(self, hass: HomeAssistant, init_data: RadioThermInitData) -> None: """Initialize DataUpdateCoordinator.""" self.init_data = init_data - self.hold_temp = hold_temp self._description = f"{init_data.name} ({init_data.host})" super().__init__( hass, @@ -39,10 +36,8 @@ class RadioThermUpdateCoordinator(DataUpdateCoordinator[RadioThermUpdate]): try: return await async_get_data(self.hass, self.init_data.tstat) except RadiothermTstatError as ex: - raise UpdateFailed( - f"{self._description} was busy (invalid value returned): {ex}" - ) from ex + msg = f"{self._description} was busy (invalid value returned): {ex}" + raise UpdateFailed(msg) from ex except timeout as ex: - raise UpdateFailed( - f"{self._description}) timed out waiting for a response: {ex}" - ) from ex + msg = f"{self._description}) timed out waiting for a response: {ex}" + raise UpdateFailed(msg) from ex diff --git a/homeassistant/components/radiotherm/entity.py b/homeassistant/components/radiotherm/entity.py new file mode 100644 index 00000000000..203d17a5dc2 --- /dev/null +++ b/homeassistant/components/radiotherm/entity.py @@ -0,0 +1,44 @@ +"""The radiotherm integration base entity.""" + +from abc import abstractmethod + +from homeassistant.core import callback +from homeassistant.helpers import device_registry as dr +from homeassistant.helpers.entity import DeviceInfo +from homeassistant.helpers.update_coordinator import CoordinatorEntity + +from .coordinator import RadioThermUpdateCoordinator +from .data import RadioThermUpdate + + +class RadioThermostatEntity(CoordinatorEntity[RadioThermUpdateCoordinator]): + """Base class for radiotherm entities.""" + + def __init__(self, coordinator: RadioThermUpdateCoordinator) -> None: + """Initialize the entity.""" + super().__init__(coordinator) + self.init_data = coordinator.init_data + self.device = coordinator.init_data.tstat + self._attr_device_info = DeviceInfo( + name=self.init_data.name, + model=self.init_data.model, + manufacturer="Radio Thermostats", + sw_version=self.init_data.fw_version, + connections={(dr.CONNECTION_NETWORK_MAC, self.init_data.mac)}, + ) + self._process_data() + + @property + def data(self) -> RadioThermUpdate: + """Returnt the last update.""" + return self.coordinator.data + + @callback + @abstractmethod + def _process_data(self) -> None: + """Update and validate the data from the thermostat.""" + + @callback + def _handle_coordinator_update(self) -> None: + self._process_data() + return super()._handle_coordinator_update() diff --git a/homeassistant/components/radiotherm/switch.py b/homeassistant/components/radiotherm/switch.py new file mode 100644 index 00000000000..2cf0602a3fa --- /dev/null +++ b/homeassistant/components/radiotherm/switch.py @@ -0,0 +1,65 @@ +"""Support for radiotherm switches.""" +from __future__ import annotations + +from typing import Any + +from homeassistant.components.switch import SwitchEntity +from homeassistant.config_entries import ConfigEntry +from homeassistant.core import HomeAssistant, callback +from homeassistant.helpers.entity_platform import AddEntitiesCallback + +from .const import DOMAIN +from .coordinator import RadioThermUpdateCoordinator +from .entity import RadioThermostatEntity + +PARALLEL_UPDATES = 1 + + +async def async_setup_entry( + hass: HomeAssistant, + entry: ConfigEntry, + async_add_entities: AddEntitiesCallback, +) -> None: + """Set up switches for a radiotherm device.""" + coordinator: RadioThermUpdateCoordinator = hass.data[DOMAIN][entry.entry_id] + async_add_entities([RadioThermHoldSwitch(coordinator)]) + + +class RadioThermHoldSwitch(RadioThermostatEntity, SwitchEntity): + """Provides radiotherm hold switch support.""" + + def __init__(self, coordinator: RadioThermUpdateCoordinator) -> None: + """Initialize the hold mode switch.""" + super().__init__(coordinator) + self._attr_name = f"{coordinator.init_data.name} Hold" + self._attr_unique_id = f"{coordinator.init_data.mac}_hold" + + @property + def icon(self) -> str: + """Return the icon for the switch.""" + return "mdi:timer-off" if self.is_on else "mdi:timer" + + @callback + def _process_data(self) -> None: + """Update and validate the data from the thermostat.""" + data = self.data.tstat + self._attr_is_on = bool(data["hold"]) + + def _set_hold(self, hold: bool) -> None: + """Set hold mode.""" + self.device.hold = int(hold) + + async def _async_set_hold(self, hold: bool) -> None: + """Set hold mode.""" + await self.hass.async_add_executor_job(self._set_hold, hold) + self._attr_is_on = hold + self.async_write_ha_state() + await self.coordinator.async_request_refresh() + + async def async_turn_on(self, **kwargs: Any) -> None: + """Enable permanent hold.""" + await self._async_set_hold(True) + + async def async_turn_off(self, **kwargs: Any) -> None: + """Disable permanent hold.""" + await self._async_set_hold(False) diff --git a/homeassistant/components/radiotherm/util.py b/homeassistant/components/radiotherm/util.py new file mode 100644 index 00000000000..85b927d7935 --- /dev/null +++ b/homeassistant/components/radiotherm/util.py @@ -0,0 +1,24 @@ +"""Utils for radiotherm.""" +from __future__ import annotations + +from radiotherm.thermostat import CommonThermostat + +from homeassistant.core import HomeAssistant +from homeassistant.util import dt as dt_util + + +async def async_set_time(hass: HomeAssistant, device: CommonThermostat) -> None: + """Sync time to the thermostat.""" + await hass.async_add_executor_job(_set_time, device) + + +def _set_time(device: CommonThermostat) -> None: + """Set device time.""" + # Calling this clears any local temperature override and + # reverts to the scheduled temperature. + now = dt_util.now() + device.time = { + "day": now.weekday(), + "hour": now.hour, + "minute": now.minute, + } diff --git a/tests/components/radiotherm/test_config_flow.py b/tests/components/radiotherm/test_config_flow.py index bc729a27e1c..56a361404f8 100644 --- a/tests/components/radiotherm/test_config_flow.py +++ b/tests/components/radiotherm/test_config_flow.py @@ -7,7 +7,7 @@ from radiotherm.validate import RadiothermTstatError from homeassistant import config_entries, data_entry_flow from homeassistant.components import dhcp -from homeassistant.components.radiotherm.const import CONF_HOLD_TEMP, DOMAIN +from homeassistant.components.radiotherm.const import DOMAIN from homeassistant.const import CONF_HOST from tests.common import MockConfigEntry @@ -110,17 +110,12 @@ async def test_import(hass): result = await hass.config_entries.flow.async_init( DOMAIN, context={"source": config_entries.SOURCE_IMPORT}, - data={CONF_HOST: "1.2.3.4", CONF_HOLD_TEMP: True}, + data={CONF_HOST: "1.2.3.4"}, ) assert result["type"] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY assert result["title"] == "My Name" - assert result["data"] == { - CONF_HOST: "1.2.3.4", - } - assert result["options"] == { - CONF_HOLD_TEMP: True, - } + assert result["data"] == {CONF_HOST: "1.2.3.4"} assert len(mock_setup_entry.mock_calls) == 1 @@ -133,7 +128,7 @@ async def test_import_cannot_connect(hass): result = await hass.config_entries.flow.async_init( DOMAIN, context={"source": config_entries.SOURCE_IMPORT}, - data={CONF_HOST: "1.2.3.4", CONF_HOLD_TEMP: True}, + data={CONF_HOST: "1.2.3.4"}, ) assert result["type"] == data_entry_flow.RESULT_TYPE_ABORT @@ -268,35 +263,3 @@ async def test_user_unique_id_already_exists(hass): assert result2["type"] == data_entry_flow.RESULT_TYPE_ABORT assert result2["reason"] == "already_configured" - - -async def test_options_flow(hass): - """Test config flow options.""" - entry = MockConfigEntry( - domain=DOMAIN, - data={CONF_HOST: "1.2.3.4"}, - unique_id="aa:bb:cc:dd:ee:ff", - options={CONF_HOLD_TEMP: False}, - ) - - entry.add_to_hass(hass) - with patch( - "homeassistant.components.radiotherm.data.radiotherm.get_thermostat", - return_value=_mock_radiotherm(), - ): - assert await hass.config_entries.async_setup(entry.entry_id) - await hass.async_block_till_done() - result = await hass.config_entries.options.async_init(entry.entry_id) - await hass.async_block_till_done() - assert await hass.config_entries.async_unload(entry.entry_id) - await hass.async_block_till_done() - - assert result["type"] == data_entry_flow.RESULT_TYPE_FORM - assert result["step_id"] == "init" - - result = await hass.config_entries.options.async_configure( - result["flow_id"], user_input={CONF_HOLD_TEMP: True} - ) - - assert result["type"] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY - assert entry.options == {CONF_HOLD_TEMP: True}