Make radiotherm hold mode a switch (#73104)
This commit is contained in:
parent
1331c75ec2
commit
329595bf73
10 changed files with 195 additions and 181 deletions
|
@ -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
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -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]
|
||||
|
|
|
@ -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
|
||||
}
|
||||
),
|
||||
)
|
||||
|
|
|
@ -2,6 +2,4 @@
|
|||
|
||||
DOMAIN = "radiotherm"
|
||||
|
||||
CONF_HOLD_TEMP = "hold_temp"
|
||||
|
||||
TIMEOUT = 25
|
||||
|
|
|
@ -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
|
||||
|
|
44
homeassistant/components/radiotherm/entity.py
Normal file
44
homeassistant/components/radiotherm/entity.py
Normal file
|
@ -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()
|
65
homeassistant/components/radiotherm/switch.py
Normal file
65
homeassistant/components/radiotherm/switch.py
Normal file
|
@ -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)
|
24
homeassistant/components/radiotherm/util.py
Normal file
24
homeassistant/components/radiotherm/util.py
Normal file
|
@ -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,
|
||||
}
|
|
@ -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}
|
||||
|
|
Loading…
Add table
Reference in a new issue