246 lines
9.2 KiB
Python
246 lines
9.2 KiB
Python
# pylint: disable=unused-argument
|
|
|
|
""" Implements the VersatileThermostat select component """
|
|
import logging
|
|
from vtherm_api.log_collector import get_vtherm_logger
|
|
|
|
from homeassistant.core import HomeAssistant, callback, Event
|
|
|
|
from homeassistant.const import EntityCategory
|
|
from homeassistant.components.select import SelectEntity
|
|
from homeassistant.helpers.device_registry import DeviceInfo, DeviceEntryType
|
|
from homeassistant.config_entries import ConfigEntry
|
|
from homeassistant.helpers.restore_state import RestoreEntity
|
|
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
|
|
|
from custom_components.versatile_thermostat.base_thermostat import (
|
|
ConfigData,
|
|
)
|
|
|
|
from custom_components.versatile_thermostat.vtherm_central_api import VersatileThermostatAPI
|
|
|
|
from .base_entity import VersatileThermostatBaseEntity
|
|
|
|
from .const import (
|
|
DOMAIN,
|
|
DEVICE_MANUFACTURER,
|
|
CONF_NAME,
|
|
CONF_THERMOSTAT_TYPE,
|
|
CONF_THERMOSTAT_CENTRAL_CONFIG,
|
|
CONF_THERMOSTAT_CLIMATE,
|
|
CONF_USE_AUTO_START_STOP_FEATURE,
|
|
CENTRAL_MODE_AUTO,
|
|
CENTRAL_MODES,
|
|
AUTO_START_STOP_STOP_MODE_OFF,
|
|
AUTO_START_STOP_STOP_MODE_FAN_ONLY,
|
|
AUTO_START_STOP_STOP_MODE_DRY,
|
|
AUTO_START_STOP_STOP_MODES,
|
|
overrides,
|
|
)
|
|
from .commons import write_event_log
|
|
|
|
_LOGGER = get_vtherm_logger(__name__)
|
|
|
|
|
|
async def async_setup_entry(
|
|
hass: HomeAssistant,
|
|
entry: ConfigEntry,
|
|
async_add_entities: AddEntitiesCallback,
|
|
) -> None:
|
|
"""Set up the VersatileThermostat selects with config flow."""
|
|
unique_id = entry.entry_id
|
|
name = entry.data.get(CONF_NAME)
|
|
_LOGGER.debug("%s - Calling async_setup_entry entry=%s, data=%s", name, entry.entry_id, entry.data)
|
|
vt_type = entry.data.get(CONF_THERMOSTAT_TYPE)
|
|
|
|
entities = []
|
|
|
|
if vt_type == CONF_THERMOSTAT_CENTRAL_CONFIG:
|
|
entities.append(CentralModeSelect(hass, unique_id, name, entry.data))
|
|
elif vt_type == CONF_THERMOSTAT_CLIMATE:
|
|
if entry.data.get(CONF_USE_AUTO_START_STOP_FEATURE) is True:
|
|
entities.append(AutoStartStopStopModeSelect(hass, unique_id, name, entry.data))
|
|
|
|
if entities:
|
|
async_add_entities(entities, True)
|
|
|
|
|
|
class CentralModeSelect(SelectEntity, RestoreEntity):
|
|
"""Representation of the central mode choice"""
|
|
|
|
def __init__(
|
|
self, hass: HomeAssistant, unique_id: str, name: str, entry_infos: ConfigData
|
|
):
|
|
"""Initialize the energy sensor"""
|
|
self._config_id = unique_id
|
|
self._device_name = entry_infos.get(CONF_NAME)
|
|
self._attr_name = "Central Mode"
|
|
self._attr_unique_id = "central_mode"
|
|
self._attr_options = CENTRAL_MODES
|
|
self._attr_current_option = CENTRAL_MODE_AUTO
|
|
|
|
@property
|
|
def icon(self) -> str:
|
|
return "mdi:form-select"
|
|
|
|
@property
|
|
def device_info(self) -> DeviceInfo:
|
|
"""Return the device info."""
|
|
return DeviceInfo(
|
|
entry_type=None,
|
|
identifiers={(DOMAIN, self._config_id)},
|
|
name=self._device_name,
|
|
manufacturer=DEVICE_MANUFACTURER,
|
|
model=DOMAIN,
|
|
)
|
|
|
|
@overrides
|
|
async def async_added_to_hass(self) -> None:
|
|
await super().async_added_to_hass()
|
|
|
|
old_state = await self.async_get_last_state()
|
|
_LOGGER.debug(
|
|
"%s - Calling async_added_to_hass old_state is %s", self, old_state
|
|
)
|
|
if old_state is not None:
|
|
self._attr_current_option = old_state.state
|
|
|
|
api: VersatileThermostatAPI = VersatileThermostatAPI.get_vtherm_api(self.hass)
|
|
api.register_central_mode_select(self)
|
|
|
|
@overrides
|
|
async def async_select_option(self, option: str) -> None:
|
|
"""Change the selected option."""
|
|
old_option = self._attr_current_option
|
|
|
|
if option == old_option:
|
|
return
|
|
|
|
if option in CENTRAL_MODES:
|
|
write_event_log(_LOGGER, self, f"Central mode is being changed from {old_option} to {option}")
|
|
self._attr_current_option = option
|
|
await self.notify_central_mode_change(old_central_mode=old_option)
|
|
|
|
@overrides
|
|
def select_option(self, option: str) -> None:
|
|
"""Change the selected option"""
|
|
# Update the VTherms which have temperature in central config
|
|
self.hass.create_task(self.async_select_option(option))
|
|
|
|
async def notify_central_mode_change(self, old_central_mode: str | None = None):
|
|
"""Notify all VTherm that the central_mode have change"""
|
|
api: VersatileThermostatAPI = VersatileThermostatAPI.get_vtherm_api(self.hass)
|
|
# Update all VTherm states
|
|
await api.notify_central_mode_change(old_central_mode)
|
|
|
|
def __str__(self) -> str:
|
|
return f"VersatileThermostat-{self.name}"
|
|
|
|
|
|
class AutoStartStopStopModeSelect(
|
|
VersatileThermostatBaseEntity, SelectEntity, RestoreEntity
|
|
):
|
|
"""Representation of the hvac_mode applied when the auto-start/stop
|
|
feature detects a stop condition (off, fan_only or dry)."""
|
|
|
|
def __init__(
|
|
self, hass: HomeAssistant, unique_id: str, name: str, entry_infos: ConfigData
|
|
):
|
|
"""Initialize the auto-start/stop stop mode select"""
|
|
super().__init__(hass, unique_id, name)
|
|
self._attr_name = "Auto start/stop stop mode"
|
|
self._attr_unique_id = f"{self._device_name}_auto_start_stop_stop_mode"
|
|
self._attr_translation_key = "auto_start_stop_stop_mode"
|
|
self._attr_entity_category = EntityCategory.CONFIG
|
|
self._attr_current_option = AUTO_START_STOP_STOP_MODE_OFF
|
|
|
|
@property
|
|
def icon(self) -> str | None:
|
|
"""The icon"""
|
|
return "mdi:hvac"
|
|
|
|
@property
|
|
def options(self) -> list[str]:
|
|
"""The available options, computed from the underlying supported hvac_modes"""
|
|
return self._build_options()
|
|
|
|
def _build_options(self) -> list[str]:
|
|
"""Build the available options from the underlying supported hvac_modes.
|
|
fan_only and dry are only proposed if the underlying supports them."""
|
|
options = [AUTO_START_STOP_STOP_MODE_OFF]
|
|
climate = self.my_climate
|
|
if climate is not None:
|
|
hvac_modes = climate.hvac_modes
|
|
if AUTO_START_STOP_STOP_MODE_FAN_ONLY in hvac_modes:
|
|
options.append(AUTO_START_STOP_STOP_MODE_FAN_ONLY)
|
|
if AUTO_START_STOP_STOP_MODE_DRY in hvac_modes:
|
|
options.append(AUTO_START_STOP_STOP_MODE_DRY)
|
|
return options
|
|
|
|
@callback
|
|
def my_climate_is_initialized(self):
|
|
"""Called when the associated climate is resolved -> refresh the options.
|
|
The restored option is only validated once the VTherm is fully
|
|
initialized (its underlying hvac_modes are available)."""
|
|
|
|
self._refresh_current_option()
|
|
self.hass.create_task(self.update_my_state_and_vtherm())
|
|
|
|
def _refresh_current_option(self):
|
|
"""Reset the current option to off only once the VTherm is fully
|
|
initialized and its underlying does not support the current option.
|
|
While the VTherm is not initialized, the restored option is kept as is
|
|
to avoid discarding it before the underlying hvac_modes are available."""
|
|
climate = self.my_climate
|
|
if climate is None or not climate.is_initialized:
|
|
return
|
|
if self._attr_current_option not in self.options:
|
|
self._attr_current_option = AUTO_START_STOP_STOP_MODE_OFF
|
|
|
|
@overrides
|
|
async def async_my_climate_changed(self, event: Event = None):
|
|
"""Called when my climate changes -> refresh the available options.
|
|
The underlying supported hvac_modes may become available only after
|
|
the VTherm has adopted them, so the options must be recomputed."""
|
|
if self.my_climate is None:
|
|
return
|
|
self._refresh_current_option()
|
|
self.async_write_ha_state()
|
|
|
|
@overrides
|
|
async def async_added_to_hass(self):
|
|
# Restore the persisted value before looking for the climate so that
|
|
# my_climate_is_initialized validates the options against it.
|
|
last_state = await self.async_get_last_state()
|
|
if last_state is not None and last_state.state in AUTO_START_STOP_STOP_MODES:
|
|
self._attr_current_option = last_state.state
|
|
|
|
await super().async_added_to_hass()
|
|
|
|
await self.update_my_state_and_vtherm()
|
|
|
|
async def update_my_state_and_vtherm(self):
|
|
"""Update the stop mode in my VTherm auto-start/stop manager"""
|
|
self.async_write_ha_state()
|
|
if (
|
|
self.my_climate is not None
|
|
and self.my_climate.auto_start_stop_manager is not None
|
|
):
|
|
await self.my_climate.auto_start_stop_manager.set_auto_start_stop_stop_mode(self._attr_current_option)
|
|
|
|
@overrides
|
|
async def async_select_option(self, option: str) -> None:
|
|
"""Change the selected option."""
|
|
if option == self._attr_current_option:
|
|
return
|
|
|
|
if option in self.options:
|
|
write_event_log(_LOGGER, self, f"Auto start/stop stop mode is being changed from {self._attr_current_option} to {option}")
|
|
self._attr_current_option = option
|
|
await self.update_my_state_and_vtherm()
|
|
|
|
@overrides
|
|
def select_option(self, option: str) -> None:
|
|
"""Change the selected option"""
|
|
self.hass.create_task(self.async_select_option(option))
|