Add availability to choose entity in Config Flow
This commit is contained in:
@@ -58,3 +58,41 @@ input_boolean:
|
||||
fake_presence_sensor1:
|
||||
name: Presence Sensor 1
|
||||
icon: mdi:home
|
||||
|
||||
climate:
|
||||
- platform: generic_thermostat
|
||||
name: Underlying thermostat1
|
||||
heater: input_boolean.fake_heater_switch3
|
||||
target_sensor: input_number.fake_temperature_sensor1
|
||||
- platform: generic_thermostat
|
||||
name: Underlying thermostat2
|
||||
heater: input_boolean.fake_heater_switch3
|
||||
target_sensor: input_number.fake_temperature_sensor1
|
||||
- platform: generic_thermostat
|
||||
name: Underlying thermostat3
|
||||
heater: input_boolean.fake_heater_switch3
|
||||
target_sensor: input_number.fake_temperature_sensor1
|
||||
- platform: generic_thermostat
|
||||
name: Underlying thermostat4
|
||||
heater: input_boolean.fake_heater_switch3
|
||||
target_sensor: input_number.fake_temperature_sensor1
|
||||
- platform: generic_thermostat
|
||||
name: Underlying thermostat5
|
||||
heater: input_boolean.fake_heater_switch3
|
||||
target_sensor: input_number.fake_temperature_sensor1
|
||||
- platform: generic_thermostat
|
||||
name: Underlying thermostat6
|
||||
heater: input_boolean.fake_heater_switch3
|
||||
target_sensor: input_number.fake_temperature_sensor1
|
||||
- platform: generic_thermostat
|
||||
name: Underlying thermostat7
|
||||
heater: input_boolean.fake_heater_switch3
|
||||
target_sensor: input_number.fake_temperature_sensor1
|
||||
- platform: generic_thermostat
|
||||
name: Underlying thermostat8
|
||||
heater: input_boolean.fake_heater_switch3
|
||||
target_sensor: input_number.fake_temperature_sensor1
|
||||
- platform: generic_thermostat
|
||||
name: Underlying thermostat9
|
||||
heater: input_boolean.fake_heater_switch3
|
||||
target_sensor: input_number.fake_temperature_sensor1
|
||||
@@ -29,20 +29,24 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
|
||||
# hass.data.setdefault(DOMAIN, {})
|
||||
|
||||
# TODO 1. Create API instance
|
||||
api: VersatileThermostatAPI = hass.data.get(DOMAIN)
|
||||
if api is None:
|
||||
api = VersatileThermostatAPI(hass)
|
||||
|
||||
# TODO 2. Validate the API connection (and authentication)
|
||||
# TODO 3. Store an API object for your platforms to access
|
||||
api.add_entry(entry)
|
||||
|
||||
entry.async_on_unload(entry.add_update_listener(update_listener))
|
||||
|
||||
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
|
||||
|
||||
return True
|
||||
|
||||
|
||||
async def update_listener(hass: HomeAssistant, entry: ConfigEntry) -> None:
|
||||
"""Update listener."""
|
||||
await hass.config_entries.async_reload(entry.entry_id)
|
||||
|
||||
|
||||
async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
"""Unload a config entry."""
|
||||
api: VersatileThermostatAPI = hass.data.get(DOMAIN)
|
||||
|
||||
@@ -18,6 +18,7 @@ from homeassistant.components.climate import ClimateEntity
|
||||
from homeassistant.helpers.restore_state import RestoreEntity
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||
from homeassistant.helpers.entity_component import EntityComponent
|
||||
|
||||
from homeassistant.helpers.event import (
|
||||
async_track_state_change_event,
|
||||
@@ -105,6 +106,10 @@ from .const import (
|
||||
CONF_TEMP_MAX,
|
||||
CONF_TEMP_MIN,
|
||||
HIDDEN_PRESETS,
|
||||
CONF_THERMOSTAT_TYPE,
|
||||
# CONF_THERMOSTAT_SWITCH,
|
||||
CONF_THERMOSTAT_CLIMATE,
|
||||
CONF_CLIMATE,
|
||||
)
|
||||
|
||||
from .prop_algorithm import PropAlgorithm
|
||||
@@ -128,7 +133,8 @@ async def async_setup_entry(
|
||||
entity = VersatileThermostat(hass, unique_id, name, entry.data)
|
||||
|
||||
async_add_entities([entity], True)
|
||||
VersatileThermostat.add_entity(entry.entry_id, entity)
|
||||
# No more needed
|
||||
# VersatileThermostat.add_entity(entry.entry_id, entity)
|
||||
|
||||
# Add services
|
||||
platform = entity_platform.async_get_current_platform()
|
||||
@@ -152,12 +158,16 @@ async def async_setup_entry(
|
||||
"service_set_preset_temperature",
|
||||
)
|
||||
|
||||
# A test to see if I'm able to get the entity
|
||||
_LOGGER.error("Plaform entities are: %s", platform.entities)
|
||||
|
||||
|
||||
class VersatileThermostat(ClimateEntity, RestoreEntity):
|
||||
"""Representation of a Versatile Thermostat device."""
|
||||
|
||||
# The list of VersatileThermostat entities
|
||||
_registry: dict[str, object] = {}
|
||||
# No more needed
|
||||
# _registry: dict[str, object] = {}
|
||||
|
||||
def __init__(self, hass, unique_id, name, entry_infos) -> None:
|
||||
"""Initialize the thermostat."""
|
||||
@@ -197,6 +207,10 @@ class VersatileThermostat(ClimateEntity, RestoreEntity):
|
||||
self._security_delay_min = None
|
||||
self._security_state = None
|
||||
|
||||
self._thermostat_type = None
|
||||
self._heater_entity_id = None
|
||||
self._climate_entity_id = None
|
||||
|
||||
self.post_init(entry_infos)
|
||||
|
||||
def post_init(self, entry_infos):
|
||||
@@ -236,7 +250,12 @@ class VersatileThermostat(ClimateEntity, RestoreEntity):
|
||||
self._motion_call_cancel = None
|
||||
|
||||
# Exploit usable attributs
|
||||
self._heater_entity_id = entry_infos.get(CONF_HEATER)
|
||||
self._thermostat_type = entry_infos.get(CONF_THERMOSTAT_TYPE)
|
||||
if self._thermostat_type == CONF_THERMOSTAT_CLIMATE:
|
||||
self._climate_entity_id = entry_infos.get(CONF_CLIMATE)
|
||||
else:
|
||||
self._heater_entity_id = entry_infos.get(CONF_HEATER)
|
||||
|
||||
self._cycle_min = entry_infos.get(CONF_CYCLE_MIN)
|
||||
self._proportional_function = entry_infos.get(CONF_PROP_FUNCTION)
|
||||
self._temp_sensor_entity_id = entry_infos.get(CONF_TEMP_SENSOR)
|
||||
@@ -377,6 +396,339 @@ class VersatileThermostat(ClimateEntity, RestoreEntity):
|
||||
self._heater_entity_id,
|
||||
)
|
||||
|
||||
async def async_added_to_hass(self):
|
||||
"""Run when entity about to be added."""
|
||||
_LOGGER.debug("Calling async_added_to_hass")
|
||||
|
||||
await super().async_added_to_hass()
|
||||
|
||||
# Add listener
|
||||
if self._thermostat_type == CONF_THERMOSTAT_CLIMATE:
|
||||
self.async_on_remove(
|
||||
async_track_state_change_event(
|
||||
self.hass, [self._climate_entity_id], self._async_climate_changed
|
||||
)
|
||||
)
|
||||
else:
|
||||
self.async_on_remove(
|
||||
async_track_state_change_event(
|
||||
self.hass, [self._heater_entity_id], self._async_switch_changed
|
||||
)
|
||||
)
|
||||
|
||||
self.async_on_remove(
|
||||
async_track_state_change_event(
|
||||
self.hass,
|
||||
[self._temp_sensor_entity_id],
|
||||
self._async_temperature_changed,
|
||||
)
|
||||
)
|
||||
|
||||
if self._ext_temp_sensor_entity_id:
|
||||
self.async_on_remove(
|
||||
async_track_state_change_event(
|
||||
self.hass,
|
||||
[self._ext_temp_sensor_entity_id],
|
||||
self._async_ext_temperature_changed,
|
||||
)
|
||||
)
|
||||
|
||||
if self._window_sensor_entity_id:
|
||||
self.async_on_remove(
|
||||
async_track_state_change_event(
|
||||
self.hass,
|
||||
[self._window_sensor_entity_id],
|
||||
self._async_windows_changed,
|
||||
)
|
||||
)
|
||||
if self._motion_sensor_entity_id:
|
||||
self.async_on_remove(
|
||||
async_track_state_change_event(
|
||||
self.hass,
|
||||
[self._motion_sensor_entity_id],
|
||||
self._async_motion_changed,
|
||||
)
|
||||
)
|
||||
|
||||
if self._power_sensor_entity_id:
|
||||
self.async_on_remove(
|
||||
async_track_state_change_event(
|
||||
self.hass,
|
||||
[self._power_sensor_entity_id],
|
||||
self._async_power_changed,
|
||||
)
|
||||
)
|
||||
|
||||
if self._max_power_sensor_entity_id:
|
||||
self.async_on_remove(
|
||||
async_track_state_change_event(
|
||||
self.hass,
|
||||
[self._max_power_sensor_entity_id],
|
||||
self._async_max_power_changed,
|
||||
)
|
||||
)
|
||||
|
||||
if self._presence_on:
|
||||
self.async_on_remove(
|
||||
async_track_state_change_event(
|
||||
self.hass,
|
||||
[self._presence_sensor_entity_id],
|
||||
self._async_presence_changed,
|
||||
)
|
||||
)
|
||||
|
||||
self.async_on_remove(self.async_remove_thermostat)
|
||||
|
||||
await self.async_startup()
|
||||
|
||||
# starts the cycle
|
||||
# if self._cycle_min:
|
||||
# self.async_on_remove(
|
||||
# async_track_time_interval(
|
||||
# self.hass,
|
||||
# self._async_control_heating,
|
||||
# interval=timedelta(minutes=self._cycle_min),
|
||||
# )
|
||||
# )
|
||||
|
||||
def async_remove_thermostat(self):
|
||||
"""Called when the thermostat will be removed"""
|
||||
_LOGGER.info("%s - Removing thermostat", self)
|
||||
if self._async_cancel_cycle:
|
||||
self._async_cancel_cycle()
|
||||
self._async_cancel_cycle = None
|
||||
|
||||
async def async_startup(self):
|
||||
"""Triggered on startup, used to get old state and set internal states accordingly"""
|
||||
_LOGGER.debug("%s - Calling async_startup", self)
|
||||
|
||||
@callback
|
||||
async def _async_startup_internal(*_):
|
||||
_LOGGER.debug("%s - Calling async_startup_internal", self)
|
||||
need_write_state = False
|
||||
temperature_state = self.hass.states.get(self._temp_sensor_entity_id)
|
||||
if temperature_state and temperature_state.state not in (
|
||||
STATE_UNAVAILABLE,
|
||||
STATE_UNKNOWN,
|
||||
):
|
||||
_LOGGER.debug(
|
||||
"%s - temperature sensor have been retrieved: %.1f",
|
||||
self,
|
||||
float(temperature_state.state),
|
||||
)
|
||||
await self._async_update_temp(temperature_state)
|
||||
need_write_state = True
|
||||
|
||||
if self._ext_temp_sensor_entity_id:
|
||||
ext_temperature_state = self.hass.states.get(
|
||||
self._ext_temp_sensor_entity_id
|
||||
)
|
||||
if ext_temperature_state and ext_temperature_state.state not in (
|
||||
STATE_UNAVAILABLE,
|
||||
STATE_UNKNOWN,
|
||||
):
|
||||
_LOGGER.debug(
|
||||
"%s - external temperature sensor have been retrieved: %.1f",
|
||||
self,
|
||||
float(ext_temperature_state.state),
|
||||
)
|
||||
await self._async_update_ext_temp(ext_temperature_state)
|
||||
else:
|
||||
_LOGGER.debug(
|
||||
"%s - external temperature sensor have NOT been retrieved cause unknown or unavailable",
|
||||
self,
|
||||
)
|
||||
else:
|
||||
_LOGGER.debug(
|
||||
"%s - external temperature sensor have NOT been retrieved cause no external sensor",
|
||||
self,
|
||||
)
|
||||
|
||||
if self._thermostat_type == CONF_THERMOSTAT_CLIMATE:
|
||||
climate_state = self.hass.states.get(self._climate_entity_id)
|
||||
if climate_state and climate_state.state not in (
|
||||
STATE_UNAVAILABLE,
|
||||
STATE_UNKNOWN,
|
||||
):
|
||||
self._hvac_mode = climate_state
|
||||
need_write_state = True
|
||||
else:
|
||||
switch_state = self.hass.states.get(self._heater_entity_id)
|
||||
if switch_state and switch_state.state not in (
|
||||
STATE_UNAVAILABLE,
|
||||
STATE_UNKNOWN,
|
||||
):
|
||||
self.hass.create_task(self._check_switch_initial_state())
|
||||
|
||||
platforms = entity_platform.async_get_platforms(
|
||||
self._hass, "versatile_thermostat"
|
||||
)
|
||||
# A test to see if I'm able to get the entity
|
||||
_LOGGER.error("Plaform entities are: %s", platforms[1].entities)
|
||||
underclimate: VersatileThermostat = platforms[1].entities[
|
||||
"climate.thermostat_2"
|
||||
]
|
||||
_LOGGER.error("plateform[1].entitie[thermostat_2 is: %s", underclimate)
|
||||
_LOGGER.error("thermostat2.preset_modes is: %s", underclimate.preset_modes)
|
||||
|
||||
component: EntityComponent[ClimateEntity] = self._hass.data["climate"]
|
||||
_LOGGER.error("component.entities is: %s", component.get_entity("climate.thermostat_2"))
|
||||
|
||||
if self._pmax_on:
|
||||
# try to acquire current power and power max
|
||||
current_power_state = self.hass.states.get(self._power_sensor_entity_id)
|
||||
if current_power_state and current_power_state.state not in (
|
||||
STATE_UNAVAILABLE,
|
||||
STATE_UNKNOWN,
|
||||
):
|
||||
self._current_power = float(current_power_state.state)
|
||||
_LOGGER.debug(
|
||||
"%s - Current power have been retrieved: %.3f",
|
||||
self,
|
||||
self._current_power,
|
||||
)
|
||||
need_write_state = True
|
||||
|
||||
# Try to acquire power max
|
||||
current_power_max_state = self.hass.states.get(
|
||||
self._max_power_sensor_entity_id
|
||||
)
|
||||
if current_power_max_state and current_power_max_state.state not in (
|
||||
STATE_UNAVAILABLE,
|
||||
STATE_UNKNOWN,
|
||||
):
|
||||
self._current_power_max = float(current_power_max_state.state)
|
||||
_LOGGER.debug(
|
||||
"%s - Current power max have been retrieved: %.3f",
|
||||
self,
|
||||
self._current_power_max,
|
||||
)
|
||||
need_write_state = True
|
||||
|
||||
# try to acquire window entity state
|
||||
if self._window_sensor_entity_id:
|
||||
window_state = self.hass.states.get(self._window_sensor_entity_id)
|
||||
if window_state and window_state.state not in (
|
||||
STATE_UNAVAILABLE,
|
||||
STATE_UNKNOWN,
|
||||
):
|
||||
self._window_state = window_state.state
|
||||
_LOGGER.debug(
|
||||
"%s - Window state have been retrieved: %s",
|
||||
self,
|
||||
self._window_state,
|
||||
)
|
||||
need_write_state = True
|
||||
|
||||
# try to acquire motion entity state
|
||||
if self._motion_sensor_entity_id:
|
||||
motion_state = self.hass.states.get(self._motion_sensor_entity_id)
|
||||
if motion_state and motion_state.state not in (
|
||||
STATE_UNAVAILABLE,
|
||||
STATE_UNKNOWN,
|
||||
):
|
||||
self._motion_state = motion_state.state
|
||||
_LOGGER.debug(
|
||||
"%s - Motion state have been retrieved: %s",
|
||||
self,
|
||||
self._motion_state,
|
||||
)
|
||||
# recalculate the right target_temp in activity mode
|
||||
self._update_motion_temp()
|
||||
need_write_state = True
|
||||
|
||||
if self._presence_on:
|
||||
# try to acquire presence entity state
|
||||
presence_state = self.hass.states.get(self._presence_sensor_entity_id)
|
||||
if presence_state and presence_state.state not in (
|
||||
STATE_UNAVAILABLE,
|
||||
STATE_UNKNOWN,
|
||||
):
|
||||
self._update_presence(presence_state.state)
|
||||
_LOGGER.debug(
|
||||
"%s - Presence have been retrieved: %s",
|
||||
self,
|
||||
presence_state.state,
|
||||
)
|
||||
need_write_state = True
|
||||
|
||||
if need_write_state:
|
||||
self.async_write_ha_state()
|
||||
self._prop_algorithm.calculate(
|
||||
self._target_temp, self._cur_temp, self._cur_ext_temp
|
||||
)
|
||||
self.hass.create_task(self._async_control_heating())
|
||||
|
||||
await self.get_my_previous_state()
|
||||
|
||||
if self.hass.state == CoreState.running:
|
||||
await _async_startup_internal()
|
||||
else:
|
||||
self.hass.bus.async_listen_once(
|
||||
EVENT_HOMEASSISTANT_START, _async_startup_internal
|
||||
)
|
||||
|
||||
async def get_my_previous_state(self):
|
||||
"""Try to get my previou state"""
|
||||
# Check If we have an old state
|
||||
old_state = await self.async_get_last_state()
|
||||
_LOGGER.debug(
|
||||
"%s - Calling get_my_previous_state old_state is %s", self, old_state
|
||||
)
|
||||
if old_state is not None:
|
||||
# If we have no initial temperature, restore
|
||||
if self._target_temp is None:
|
||||
# If we have a previously saved temperature
|
||||
if old_state.attributes.get(ATTR_TEMPERATURE) is None:
|
||||
if self._ac_mode:
|
||||
self._target_temp = self.max_temp
|
||||
else:
|
||||
self._target_temp = self.min_temp
|
||||
_LOGGER.warning(
|
||||
"%s - Undefined target temperature, falling back to %s",
|
||||
self,
|
||||
self._target_temp,
|
||||
)
|
||||
else:
|
||||
self._target_temp = float(old_state.attributes[ATTR_TEMPERATURE])
|
||||
|
||||
if old_state.attributes.get(ATTR_PRESET_MODE) in self._attr_preset_modes:
|
||||
self._attr_preset_mode = old_state.attributes.get(ATTR_PRESET_MODE)
|
||||
self.save_preset_mode()
|
||||
|
||||
if not self._hvac_mode and old_state.state:
|
||||
self._hvac_mode = old_state.state
|
||||
|
||||
# is done in startup above
|
||||
# self._prop_algorithm.calculate(
|
||||
# self._target_temp, self._cur_temp, self._cur_ext_temp
|
||||
# )
|
||||
|
||||
else:
|
||||
# No previous state, try and restore defaults
|
||||
if self._target_temp is None:
|
||||
if self._ac_mode:
|
||||
self._target_temp = self.max_temp
|
||||
else:
|
||||
self._target_temp = self.min_temp
|
||||
_LOGGER.warning(
|
||||
"No previously saved temperature, setting to %s", self._target_temp
|
||||
)
|
||||
|
||||
self._saved_target_temp = self._target_temp
|
||||
|
||||
# Set default state to off
|
||||
if not self._hvac_mode:
|
||||
self._hvac_mode = HVAC_MODE_OFF
|
||||
|
||||
_LOGGER.info(
|
||||
"%s - restored state is target_temp=%.1f, preset_mode=%s, hvac_mode=%s",
|
||||
self,
|
||||
self._target_temp,
|
||||
self._attr_preset_mode,
|
||||
self._hvac_mode,
|
||||
)
|
||||
|
||||
def __str__(self):
|
||||
return f"VersatileThermostat-{self.name}"
|
||||
|
||||
@@ -561,300 +913,6 @@ class VersatileThermostat(ClimateEntity, RestoreEntity):
|
||||
"""Called when the entry have changed in ConfigFlow"""
|
||||
_LOGGER.info("%s - Change entry with the values: %s", self, config_entry.data)
|
||||
|
||||
async def async_added_to_hass(self):
|
||||
"""Run when entity about to be added."""
|
||||
_LOGGER.debug("Calling async_added_to_hass")
|
||||
|
||||
await super().async_added_to_hass()
|
||||
|
||||
# Add listener
|
||||
self.async_on_remove(
|
||||
async_track_state_change_event(
|
||||
self.hass, [self._heater_entity_id], self._async_switch_changed
|
||||
)
|
||||
)
|
||||
|
||||
self.async_on_remove(
|
||||
async_track_state_change_event(
|
||||
self.hass,
|
||||
[self._temp_sensor_entity_id],
|
||||
self._async_temperature_changed,
|
||||
)
|
||||
)
|
||||
|
||||
if self._ext_temp_sensor_entity_id:
|
||||
self.async_on_remove(
|
||||
async_track_state_change_event(
|
||||
self.hass,
|
||||
[self._ext_temp_sensor_entity_id],
|
||||
self._async_ext_temperature_changed,
|
||||
)
|
||||
)
|
||||
|
||||
if self._window_sensor_entity_id:
|
||||
self.async_on_remove(
|
||||
async_track_state_change_event(
|
||||
self.hass,
|
||||
[self._window_sensor_entity_id],
|
||||
self._async_windows_changed,
|
||||
)
|
||||
)
|
||||
if self._motion_sensor_entity_id:
|
||||
self.async_on_remove(
|
||||
async_track_state_change_event(
|
||||
self.hass,
|
||||
[self._motion_sensor_entity_id],
|
||||
self._async_motion_changed,
|
||||
)
|
||||
)
|
||||
|
||||
if self._power_sensor_entity_id:
|
||||
self.async_on_remove(
|
||||
async_track_state_change_event(
|
||||
self.hass,
|
||||
[self._power_sensor_entity_id],
|
||||
self._async_power_changed,
|
||||
)
|
||||
)
|
||||
|
||||
if self._max_power_sensor_entity_id:
|
||||
self.async_on_remove(
|
||||
async_track_state_change_event(
|
||||
self.hass,
|
||||
[self._max_power_sensor_entity_id],
|
||||
self._async_max_power_changed,
|
||||
)
|
||||
)
|
||||
|
||||
if self._presence_on:
|
||||
self.async_on_remove(
|
||||
async_track_state_change_event(
|
||||
self.hass,
|
||||
[self._presence_sensor_entity_id],
|
||||
self._async_presence_changed,
|
||||
)
|
||||
)
|
||||
|
||||
await self.async_startup()
|
||||
|
||||
# starts the cycle
|
||||
# if self._cycle_min:
|
||||
# self.async_on_remove(
|
||||
# async_track_time_interval(
|
||||
# self.hass,
|
||||
# self._async_control_heating,
|
||||
# interval=timedelta(minutes=self._cycle_min),
|
||||
# )
|
||||
# )
|
||||
|
||||
async def async_startup(self):
|
||||
"""Triggered on startup, used to get old state and set internal states accordingly"""
|
||||
_LOGGER.debug("%s - Calling async_startup", self)
|
||||
|
||||
@callback
|
||||
async def _async_startup_internal(*_):
|
||||
_LOGGER.debug("%s - Calling async_startup_internal", self)
|
||||
need_write_state = False
|
||||
temperature_state = self.hass.states.get(self._temp_sensor_entity_id)
|
||||
if temperature_state and temperature_state.state not in (
|
||||
STATE_UNAVAILABLE,
|
||||
STATE_UNKNOWN,
|
||||
):
|
||||
_LOGGER.debug(
|
||||
"%s - temperature sensor have been retrieved: %.1f",
|
||||
self,
|
||||
float(temperature_state.state),
|
||||
)
|
||||
await self._async_update_temp(temperature_state)
|
||||
need_write_state = True
|
||||
|
||||
if self._ext_temp_sensor_entity_id:
|
||||
ext_temperature_state = self.hass.states.get(
|
||||
self._ext_temp_sensor_entity_id
|
||||
)
|
||||
if ext_temperature_state and ext_temperature_state.state not in (
|
||||
STATE_UNAVAILABLE,
|
||||
STATE_UNKNOWN,
|
||||
):
|
||||
_LOGGER.debug(
|
||||
"%s - external temperature sensor have been retrieved: %.1f",
|
||||
self,
|
||||
float(ext_temperature_state.state),
|
||||
)
|
||||
await self._async_update_ext_temp(ext_temperature_state)
|
||||
else:
|
||||
_LOGGER.debug(
|
||||
"%s - external temperature sensor have NOT been retrieved cause unknown or unavailable",
|
||||
self,
|
||||
)
|
||||
else:
|
||||
_LOGGER.debug(
|
||||
"%s - external temperature sensor have NOT been retrieved cause no external sensor",
|
||||
self,
|
||||
)
|
||||
|
||||
switch_state = self.hass.states.get(self._heater_entity_id)
|
||||
if switch_state and switch_state.state not in (
|
||||
STATE_UNAVAILABLE,
|
||||
STATE_UNKNOWN,
|
||||
):
|
||||
self.hass.create_task(self._check_switch_initial_state())
|
||||
|
||||
if self._pmax_on:
|
||||
# try to acquire current power and power max
|
||||
current_power_state = self.hass.states.get(self._power_sensor_entity_id)
|
||||
if current_power_state and current_power_state.state not in (
|
||||
STATE_UNAVAILABLE,
|
||||
STATE_UNKNOWN,
|
||||
):
|
||||
self._current_power = float(current_power_state.state)
|
||||
_LOGGER.debug(
|
||||
"%s - Current power have been retrieved: %.3f",
|
||||
self,
|
||||
self._current_power,
|
||||
)
|
||||
need_write_state = True
|
||||
|
||||
# Try to acquire power max
|
||||
current_power_max_state = self.hass.states.get(
|
||||
self._max_power_sensor_entity_id
|
||||
)
|
||||
if current_power_max_state and current_power_max_state.state not in (
|
||||
STATE_UNAVAILABLE,
|
||||
STATE_UNKNOWN,
|
||||
):
|
||||
self._current_power_max = float(current_power_max_state.state)
|
||||
_LOGGER.debug(
|
||||
"%s - Current power max have been retrieved: %.3f",
|
||||
self,
|
||||
self._current_power_max,
|
||||
)
|
||||
need_write_state = True
|
||||
|
||||
# try to acquire window entity state
|
||||
if self._window_sensor_entity_id:
|
||||
window_state = self.hass.states.get(self._window_sensor_entity_id)
|
||||
if window_state and window_state.state not in (
|
||||
STATE_UNAVAILABLE,
|
||||
STATE_UNKNOWN,
|
||||
):
|
||||
self._window_state = window_state.state
|
||||
_LOGGER.debug(
|
||||
"%s - Window state have been retrieved: %s",
|
||||
self,
|
||||
self._window_state,
|
||||
)
|
||||
need_write_state = True
|
||||
|
||||
# try to acquire motion entity state
|
||||
if self._motion_sensor_entity_id:
|
||||
motion_state = self.hass.states.get(self._motion_sensor_entity_id)
|
||||
if motion_state and motion_state.state not in (
|
||||
STATE_UNAVAILABLE,
|
||||
STATE_UNKNOWN,
|
||||
):
|
||||
self._motion_state = motion_state.state
|
||||
_LOGGER.debug(
|
||||
"%s - Motion state have been retrieved: %s",
|
||||
self,
|
||||
self._motion_state,
|
||||
)
|
||||
# recalculate the right target_temp in activity mode
|
||||
self._update_motion_temp()
|
||||
need_write_state = True
|
||||
|
||||
if self._presence_on:
|
||||
# try to acquire presence entity state
|
||||
presence_state = self.hass.states.get(self._presence_sensor_entity_id)
|
||||
if presence_state and presence_state.state not in (
|
||||
STATE_UNAVAILABLE,
|
||||
STATE_UNKNOWN,
|
||||
):
|
||||
self._update_presence(presence_state.state)
|
||||
_LOGGER.debug(
|
||||
"%s - Presence have been retrieved: %s",
|
||||
self,
|
||||
presence_state.state,
|
||||
)
|
||||
need_write_state = True
|
||||
|
||||
if need_write_state:
|
||||
self.async_write_ha_state()
|
||||
self._prop_algorithm.calculate(
|
||||
self._target_temp, self._cur_temp, self._cur_ext_temp
|
||||
)
|
||||
self.hass.create_task(self._async_control_heating())
|
||||
|
||||
await self.get_my_previous_state()
|
||||
|
||||
if self.hass.state == CoreState.running:
|
||||
await _async_startup_internal()
|
||||
else:
|
||||
self.hass.bus.async_listen_once(
|
||||
EVENT_HOMEASSISTANT_START, _async_startup_internal
|
||||
)
|
||||
|
||||
async def get_my_previous_state(self):
|
||||
"""Try to get my previou state"""
|
||||
# Check If we have an old state
|
||||
old_state = await self.async_get_last_state()
|
||||
_LOGGER.debug(
|
||||
"%s - Calling get_my_previous_state old_state is %s", self, old_state
|
||||
)
|
||||
if old_state is not None:
|
||||
# If we have no initial temperature, restore
|
||||
if self._target_temp is None:
|
||||
# If we have a previously saved temperature
|
||||
if old_state.attributes.get(ATTR_TEMPERATURE) is None:
|
||||
if self._ac_mode:
|
||||
self._target_temp = self.max_temp
|
||||
else:
|
||||
self._target_temp = self.min_temp
|
||||
_LOGGER.warning(
|
||||
"%s - Undefined target temperature, falling back to %s",
|
||||
self,
|
||||
self._target_temp,
|
||||
)
|
||||
else:
|
||||
self._target_temp = float(old_state.attributes[ATTR_TEMPERATURE])
|
||||
|
||||
if old_state.attributes.get(ATTR_PRESET_MODE) in self._attr_preset_modes:
|
||||
self._attr_preset_mode = old_state.attributes.get(ATTR_PRESET_MODE)
|
||||
self.save_preset_mode()
|
||||
|
||||
if not self._hvac_mode and old_state.state:
|
||||
self._hvac_mode = old_state.state
|
||||
|
||||
# is done in startup above
|
||||
# self._prop_algorithm.calculate(
|
||||
# self._target_temp, self._cur_temp, self._cur_ext_temp
|
||||
# )
|
||||
|
||||
else:
|
||||
# No previous state, try and restore defaults
|
||||
if self._target_temp is None:
|
||||
if self._ac_mode:
|
||||
self._target_temp = self.max_temp
|
||||
else:
|
||||
self._target_temp = self.min_temp
|
||||
_LOGGER.warning(
|
||||
"No previously saved temperature, setting to %s", self._target_temp
|
||||
)
|
||||
|
||||
self._saved_target_temp = self._target_temp
|
||||
|
||||
# Set default state to off
|
||||
if not self._hvac_mode:
|
||||
self._hvac_mode = HVAC_MODE_OFF
|
||||
|
||||
_LOGGER.info(
|
||||
"%s - restored state is target_temp=%.1f, preset_mode=%s, hvac_mode=%s",
|
||||
self,
|
||||
self._target_temp,
|
||||
self._attr_preset_mode,
|
||||
self._hvac_mode,
|
||||
)
|
||||
|
||||
@callback
|
||||
async def _async_temperature_changed(self, event):
|
||||
"""Handle temperature changes."""
|
||||
@@ -1289,7 +1347,7 @@ class VersatileThermostat(ClimateEntity, RestoreEntity):
|
||||
|
||||
return True
|
||||
|
||||
async def _async_control_heating(self, force=False, time=None):
|
||||
async def _async_control_heating(self, force=False, _=None):
|
||||
"""The main function used to run the calculation at each cycle"""
|
||||
|
||||
overpowering: bool = await self.check_overpowering()
|
||||
@@ -1526,25 +1584,28 @@ class VersatileThermostat(ClimateEntity, RestoreEntity):
|
||||
await self._async_set_preset_mode_internal(preset, force=True)
|
||||
await self._async_control_heating(force=True)
|
||||
|
||||
@classmethod
|
||||
def add_entity(cls, entry_id, entity):
|
||||
"""Adds an entity into the VersatileRegistry entities"""
|
||||
_LOGGER.debug("Adding entity %s", entry_id)
|
||||
cls._registry[entry_id] = entity
|
||||
_LOGGER.debug("Entity registry is now %s", cls._registry)
|
||||
# No more needed
|
||||
|
||||
@classmethod
|
||||
async def update_entity(cls, entry_id, infos):
|
||||
"""Updates an existing entity referenced by entry_id with the infos in arguments"""
|
||||
entity: VersatileThermostat = cls._registry.get(entry_id)
|
||||
if entity is None:
|
||||
_LOGGER.warning(
|
||||
"Tries to update VersatileThermostat entity %s but was not found in thermostat registry",
|
||||
entry_id,
|
||||
)
|
||||
return
|
||||
|
||||
_LOGGER.debug("We have found the entity to update")
|
||||
entity.post_init(infos)
|
||||
|
||||
await entity.async_added_to_hass()
|
||||
# @classmethod
|
||||
# def add_entity(cls, entry_id, entity):
|
||||
# """Adds an entity into the VersatileRegistry entities"""
|
||||
# _LOGGER.debug("Adding entity %s", entry_id)
|
||||
# cls._registry[entry_id] = entity
|
||||
# _LOGGER.debug("Entity registry is now %s", cls._registry)
|
||||
#
|
||||
# @classmethod
|
||||
# async def update_entity(cls, entry_id, infos):
|
||||
# """Updates an existing entity referenced by entry_id with the infos in arguments"""
|
||||
# entity: VersatileThermostat = cls._registry.get(entry_id)
|
||||
# if entity is None:
|
||||
# _LOGGER.warning(
|
||||
# "Tries to update VersatileThermostat entity %s but was not found in thermostat registry",
|
||||
# entry_id,
|
||||
# )
|
||||
# return
|
||||
#
|
||||
# _LOGGER.debug("We have found the entity to update")
|
||||
# entity.post_init(infos)
|
||||
#
|
||||
# await entity.async_added_to_hass()
|
||||
|
||||
@@ -1,26 +1,43 @@
|
||||
"""Config flow for Versatile Thermostat integration."""
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Any
|
||||
import logging
|
||||
import copy
|
||||
from collections.abc import Mapping
|
||||
import voluptuous as vol
|
||||
|
||||
from typing import Any
|
||||
|
||||
from homeassistant.core import callback
|
||||
from homeassistant.core import callback, async_get_hass
|
||||
from homeassistant.config_entries import (
|
||||
ConfigEntry,
|
||||
ConfigFlow as HAConfigFlow,
|
||||
OptionsFlow,
|
||||
)
|
||||
|
||||
# import homeassistant.helpers.entity_registry as entity_registry
|
||||
from homeassistant.data_entry_flow import FlowHandler
|
||||
from homeassistant.data_entry_flow import FlowResult
|
||||
from homeassistant.exceptions import HomeAssistantError
|
||||
|
||||
import homeassistant.helpers.config_validation as cv
|
||||
from homeassistant.helpers.entity_component import EntityComponent
|
||||
from homeassistant.helpers.entity_registry import EntityRegistry, async_get
|
||||
from homeassistant.components.climate import ClimateEntity
|
||||
from homeassistant.components.climate.const import DOMAIN as CLIMATE_DOMAIN
|
||||
from homeassistant.components.switch import SwitchEntity
|
||||
from homeassistant.components.switch.const import DOMAIN as SWITCH_DOMAIN
|
||||
from homeassistant.components.input_boolean import (
|
||||
InputBoolean,
|
||||
DOMAIN as INPUT_BOOLEAN_DOMAIN,
|
||||
)
|
||||
|
||||
from homeassistant.components.sensor import SensorEntity
|
||||
from homeassistant.components.sensor.const import DOMAIN as SENSOR_DOMAIN
|
||||
from homeassistant.components.input_number import (
|
||||
InputNumber,
|
||||
DOMAIN as INPUT_NUMBER_DOMAIN,
|
||||
)
|
||||
|
||||
|
||||
from .const import (
|
||||
DOMAIN,
|
||||
@@ -51,12 +68,16 @@ from .const import (
|
||||
CONF_MINIMAL_ACTIVATION_DELAY,
|
||||
CONF_TEMP_MAX,
|
||||
CONF_TEMP_MIN,
|
||||
CONF_THERMOSTAT_TYPE,
|
||||
CONF_THERMOSTAT_SWITCH,
|
||||
CONF_CLIMATE,
|
||||
CONF_USE_WINDOW_FEATURE,
|
||||
CONF_USE_MOTION_FEATURE,
|
||||
CONF_USE_PRESENCE_FEATURE,
|
||||
CONF_USE_POWER_FEATURE,
|
||||
CONF_THERMOSTAT_TYPES,
|
||||
)
|
||||
|
||||
from .climate import VersatileThermostat
|
||||
|
||||
# from .climate import VersatileThermostat
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@@ -111,13 +132,46 @@ class VersatileThermostatBaseConfigFlow(FlowHandler):
|
||||
super().__init__()
|
||||
_LOGGER.debug("CTOR BaseConfigFlow infos: %s", infos)
|
||||
self._infos = infos
|
||||
self.hass = async_get_hass()
|
||||
ent_reg = async_get(hass=self.hass)
|
||||
|
||||
climates = [] # self.find_all_climates()
|
||||
switches = [] # self.find_all_heaters()
|
||||
temp_sensors = [] # self.find_all_temperature_sensors()
|
||||
|
||||
k: str
|
||||
for k in ent_reg.entities:
|
||||
v = ent_reg.entities[k]
|
||||
if k.startswith(CLIMATE_DOMAIN):
|
||||
climates.append(k)
|
||||
elif k.startswith(SWITCH_DOMAIN) or k.startswith(INPUT_BOOLEAN_DOMAIN):
|
||||
switches.append(k)
|
||||
elif k.startswith(INPUT_NUMBER_DOMAIN):
|
||||
temp_sensors.append(k)
|
||||
elif k.startswith(SENSOR_DOMAIN):
|
||||
_LOGGER.debug("We have found sensor: %s", v)
|
||||
temp_sensors.append(k)
|
||||
|
||||
self.STEP_USER_DATA_SCHEMA = vol.Schema(
|
||||
{
|
||||
vol.Required(CONF_NAME): cv.string,
|
||||
vol.Required(CONF_HEATER): cv.string,
|
||||
vol.Required(CONF_TEMP_SENSOR): cv.string,
|
||||
vol.Required(CONF_EXTERNAL_TEMP_SENSOR): cv.string,
|
||||
vol.Required(CONF_CYCLE_MIN, default=5): cv.positive_int,
|
||||
vol.Required(
|
||||
CONF_THERMOSTAT_TYPE, default=CONF_THERMOSTAT_SWITCH
|
||||
): vol.In(CONF_THERMOSTAT_TYPES),
|
||||
vol.Required(CONF_TEMP_SENSOR): vol.In(temp_sensors),
|
||||
vol.Required(CONF_EXTERNAL_TEMP_SENSOR): vol.In(temp_sensors),
|
||||
vol.Required(CONF_TEMP_MIN, default=7): vol.Coerce(float),
|
||||
vol.Required(CONF_TEMP_MAX, default=35): vol.Coerce(float),
|
||||
vol.Optional(CONF_USE_WINDOW_FEATURE, default=False): cv.boolean,
|
||||
vol.Optional(CONF_USE_MOTION_FEATURE, default=False): cv.boolean,
|
||||
vol.Optional(CONF_USE_POWER_FEATURE, default=False): cv.boolean,
|
||||
vol.Optional(CONF_USE_PRESENCE_FEATURE, default=False): cv.boolean,
|
||||
}
|
||||
)
|
||||
|
||||
self.STEP_THERMOSTAT_SWITCH = vol.Schema(
|
||||
{
|
||||
vol.Required(CONF_HEATER): vol.In(switches),
|
||||
vol.Required(
|
||||
CONF_PROP_FUNCTION, default=PROPORTIONAL_FUNCTION_TPI
|
||||
): vol.In(
|
||||
@@ -125,8 +179,13 @@ class VersatileThermostatBaseConfigFlow(FlowHandler):
|
||||
PROPORTIONAL_FUNCTION_TPI,
|
||||
]
|
||||
),
|
||||
vol.Required(CONF_TEMP_MIN, default=7): vol.Coerce(float),
|
||||
vol.Required(CONF_TEMP_MAX, default=35): vol.Coerce(float),
|
||||
vol.Required(CONF_CYCLE_MIN, default=5): cv.positive_int,
|
||||
}
|
||||
)
|
||||
|
||||
self.STEP_THERMOSTAT_CLIMATE = vol.Schema(
|
||||
{
|
||||
vol.Required(CONF_CLIMATE): vol.In(climates),
|
||||
}
|
||||
)
|
||||
|
||||
@@ -209,6 +268,7 @@ class VersatileThermostatBaseConfigFlow(FlowHandler):
|
||||
CONF_POWER_SENSOR,
|
||||
CONF_MAX_POWER_SENSOR,
|
||||
CONF_PRESENCE_SENSOR,
|
||||
CONF_CLIMATE,
|
||||
]:
|
||||
d = data.get(conf, None) # pylint: disable=invalid-name
|
||||
if d is not None and self.hass.states.get(d) is None:
|
||||
@@ -268,9 +328,25 @@ class VersatileThermostatBaseConfigFlow(FlowHandler):
|
||||
_LOGGER.debug("Into ConfigFlow.async_step_user user_input=%s", user_input)
|
||||
|
||||
return await self.generic_step(
|
||||
"user", self.STEP_USER_DATA_SCHEMA, user_input, self.async_step_tpi
|
||||
"user", self.STEP_USER_DATA_SCHEMA, user_input, self.async_step_type
|
||||
)
|
||||
|
||||
async def async_step_type(self, user_input: dict | None = None) -> FlowResult:
|
||||
"""Handle the flow steps"""
|
||||
_LOGGER.debug("Into ConfigFlow.async_step_type user_input=%s", user_input)
|
||||
|
||||
if self._infos[CONF_THERMOSTAT_TYPE] == CONF_THERMOSTAT_SWITCH:
|
||||
return await self.generic_step(
|
||||
"type", self.STEP_THERMOSTAT_SWITCH, user_input, self.async_step_tpi
|
||||
)
|
||||
else:
|
||||
return await self.generic_step(
|
||||
"type",
|
||||
self.STEP_THERMOSTAT_CLIMATE,
|
||||
user_input,
|
||||
self.async_step_presets,
|
||||
)
|
||||
|
||||
async def async_step_tpi(self, user_input: dict | None = None) -> FlowResult:
|
||||
"""Handle the flow steps"""
|
||||
_LOGGER.debug("Into ConfigFlow.async_step_tpi user_input=%s", user_input)
|
||||
@@ -283,35 +359,63 @@ class VersatileThermostatBaseConfigFlow(FlowHandler):
|
||||
"""Handle the presets flow steps"""
|
||||
_LOGGER.debug("Into ConfigFlow.async_step_presets user_input=%s", user_input)
|
||||
|
||||
next_step = self.async_step_advanced
|
||||
if self._infos[CONF_USE_WINDOW_FEATURE]:
|
||||
next_step = self.async_step_window
|
||||
elif self._infos[CONF_USE_MOTION_FEATURE]:
|
||||
next_step = self.async_step_motion
|
||||
elif self._infos[CONF_USE_POWER_FEATURE]:
|
||||
next_step = self.async_step_power
|
||||
elif self._infos[CONF_USE_PRESENCE_FEATURE]:
|
||||
next_step = self.async_step_presence
|
||||
|
||||
return await self.generic_step(
|
||||
"presets", self.STEP_PRESETS_DATA_SCHEMA, user_input, self.async_step_window
|
||||
"presets", self.STEP_PRESETS_DATA_SCHEMA, user_input, next_step
|
||||
)
|
||||
|
||||
async def async_step_window(self, user_input: dict | None = None) -> FlowResult:
|
||||
"""Handle the window sensor flow steps"""
|
||||
_LOGGER.debug("Into ConfigFlow.async_step_window user_input=%s", user_input)
|
||||
|
||||
next_step = self.async_step_advanced
|
||||
if self._infos[CONF_USE_MOTION_FEATURE]:
|
||||
next_step = self.async_step_motion
|
||||
elif self._infos[CONF_USE_POWER_FEATURE]:
|
||||
next_step = self.async_step_power
|
||||
elif self._infos[CONF_USE_PRESENCE_FEATURE]:
|
||||
next_step = self.async_step_presence
|
||||
|
||||
return await self.generic_step(
|
||||
"window", self.STEP_WINDOW_DATA_SCHEMA, user_input, self.async_step_motion
|
||||
"window", self.STEP_WINDOW_DATA_SCHEMA, user_input, next_step
|
||||
)
|
||||
|
||||
async def async_step_motion(self, user_input: dict | None = None) -> FlowResult:
|
||||
"""Handle the window and motion sensor flow steps"""
|
||||
_LOGGER.debug("Into ConfigFlow.async_step_motion user_input=%s", user_input)
|
||||
|
||||
next_step = self.async_step_advanced
|
||||
if self._infos[CONF_USE_POWER_FEATURE]:
|
||||
next_step = self.async_step_power
|
||||
elif self._infos[CONF_USE_PRESENCE_FEATURE]:
|
||||
next_step = self.async_step_presence
|
||||
|
||||
return await self.generic_step(
|
||||
"motion", self.STEP_MOTION_DATA_SCHEMA, user_input, self.async_step_power
|
||||
"motion", self.STEP_MOTION_DATA_SCHEMA, user_input, next_step
|
||||
)
|
||||
|
||||
async def async_step_power(self, user_input: dict | None = None) -> FlowResult:
|
||||
"""Handle the power management flow steps"""
|
||||
_LOGGER.debug("Into ConfigFlow.async_step_power user_input=%s", user_input)
|
||||
|
||||
next_step = self.async_step_advanced
|
||||
if self._infos[CONF_USE_PRESENCE_FEATURE]:
|
||||
next_step = self.async_step_presence
|
||||
|
||||
return await self.generic_step(
|
||||
"power",
|
||||
self.STEP_POWER_DATA_SCHEMA,
|
||||
user_input,
|
||||
self.async_step_presence,
|
||||
next_step,
|
||||
)
|
||||
|
||||
async def async_step_presence(self, user_input: dict | None = None) -> FlowResult:
|
||||
@@ -336,6 +440,45 @@ class VersatileThermostatBaseConfigFlow(FlowHandler):
|
||||
self.async_finalize, # pylint: disable=no-member
|
||||
)
|
||||
|
||||
async def async_finalize(self):
|
||||
"""Should be implemented by Leaf classes"""
|
||||
raise HomeAssistantError(
|
||||
"async_finalize not implemented on VersatileThermostat sub-class"
|
||||
)
|
||||
|
||||
def find_all_climates(self) -> list(str):
|
||||
"""Find all climate known by HA"""
|
||||
component: EntityComponent[ClimateEntity] = self.hass.data[CLIMATE_DOMAIN]
|
||||
ret: list(str) = list()
|
||||
for entity in component.entities:
|
||||
ret.append(entity.entity_id)
|
||||
_LOGGER.debug("Found all climate entities: %s", ret)
|
||||
return ret
|
||||
|
||||
def find_all_heaters(self) -> list(str):
|
||||
"""Find all heater known by HA"""
|
||||
component: EntityComponent[SwitchEntity] = self.hass.data[SWITCH_DOMAIN]
|
||||
ret: list(str) = list()
|
||||
for entity in component.entities:
|
||||
ret.append(entity.entity_id)
|
||||
# component = self.hass.data[INPUT_BOOLEAN_DOMAIN]
|
||||
# for entity in component.entities:
|
||||
# ret.append(entity.entity_id)
|
||||
_LOGGER.debug("Found all switch entities: %s", ret)
|
||||
return ret
|
||||
|
||||
def find_all_temperature_sensors(self) -> list(str):
|
||||
"""Find all heater known by HA"""
|
||||
component: EntityComponent[SensorEntity] = self.hass.data[SENSOR_DOMAIN]
|
||||
ret: list(str) = list()
|
||||
for entity in component.entities:
|
||||
ret.append(entity.entity_id)
|
||||
# component = self.hass.data[INPUT_NUMBER_DOMAIN]
|
||||
# for entity in component.entities:
|
||||
# ret.append(entity.entity_id)
|
||||
_LOGGER.debug("Found all temperature sensore entities: %s", ret)
|
||||
return ret
|
||||
|
||||
|
||||
class VersatileThermostatConfigFlow(
|
||||
VersatileThermostatBaseConfigFlow, HAConfigFlow, domain=DOMAIN
|
||||
@@ -479,33 +622,33 @@ class VersatileThermostatOptionsFlowHandler(
|
||||
async def async_end(self):
|
||||
"""Finalization of the ConfigEntry creation"""
|
||||
_LOGGER.debug(
|
||||
"CTOR ConfigFlow.async_finalize - updating entry with: %s", self._infos
|
||||
"ConfigFlow.async_finalize - updating entry with: %s", self._infos
|
||||
)
|
||||
# Find eventual existing entity to update it
|
||||
# removing entities from registry (they will be recreated)
|
||||
|
||||
# No need to do that. Only the update_listener on __init__.py is necessary
|
||||
# ent_reg = entity_registry.async_get(self.hass)
|
||||
|
||||
# reg_entities = {
|
||||
# ent.unique_id: ent.entity_id
|
||||
# for ent in entity_registry.async_entries_for_config_entry(
|
||||
# ent_reg, self.config_entry.entry_id
|
||||
# )
|
||||
# }
|
||||
#
|
||||
# for entry in entity_registry.async_entries_for_config_entry(
|
||||
# ent_reg, self.config_entry.entry_id
|
||||
# ):
|
||||
# entity: VersatileThermostat = ent_reg.async_get(entry.entity_id)
|
||||
# entity.async_registry_entry_updated(self._infos)
|
||||
# _LOGGER.info(
|
||||
# "Removing entity %s due to configuration change", entry.entity_id
|
||||
# )
|
||||
# ent_reg.async_remove(entry.entity_id)
|
||||
|
||||
_LOGGER.debug(
|
||||
"We have found entities to update: %s", self.config_entry.entry_id
|
||||
)
|
||||
await VersatileThermostat.update_entity(self.config_entry.entry_id, self._infos)
|
||||
# _LOGGER.debug(
|
||||
# "We have found entities to update: %s", self.config_entry.entry_id
|
||||
# )
|
||||
# await VersatileThermostat.update_entity(self.config_entry.entry_id, self._infos)
|
||||
|
||||
# for entity_id in reg_entities.values():
|
||||
# _LOGGER.info("Recreating entity %s due to configuration change", entity_id)
|
||||
# ent_reg.async_remove(entity_id)
|
||||
#
|
||||
_LOGGER.info(
|
||||
"Recreating entry %s due to configuration change",
|
||||
self.config_entry.entry_id,
|
||||
)
|
||||
self.hass.config_entries.async_update_entry(self.config_entry, data=self._infos)
|
||||
return self.async_create_entry(title=None, data=None)
|
||||
|
||||
@@ -42,6 +42,14 @@ CONF_MINIMAL_ACTIVATION_DELAY = "minimal_activation_delay"
|
||||
CONF_TEMP_MIN = "temp_min"
|
||||
CONF_TEMP_MAX = "temp_max"
|
||||
CONF_SECURITY_DELAY_MIN = "security_delay_min"
|
||||
CONF_THERMOSTAT_TYPE = "thermostat_type"
|
||||
CONF_THERMOSTAT_SWITCH = "thermostat_over_switch"
|
||||
CONF_THERMOSTAT_CLIMATE = "thermostat_over_climate"
|
||||
CONF_CLIMATE = "climate_entity_id"
|
||||
CONF_USE_WINDOW_FEATURE = "use_window_feature"
|
||||
CONF_USE_MOTION_FEATURE = "use_motion_feature"
|
||||
CONF_USE_PRESENCE_FEATURE = "use_presence_feature"
|
||||
CONF_USE_POWER_FEATURE = "use_power_feature"
|
||||
|
||||
CONF_PRESETS = {
|
||||
p: f"{p}_temp"
|
||||
@@ -92,6 +100,14 @@ ALL_CONF = (
|
||||
CONF_TEMP_MIN,
|
||||
CONF_TEMP_MAX,
|
||||
CONF_SECURITY_DELAY_MIN,
|
||||
CONF_THERMOSTAT_TYPE,
|
||||
CONF_THERMOSTAT_SWITCH,
|
||||
CONF_THERMOSTAT_CLIMATE,
|
||||
CONF_CLIMATE,
|
||||
CONF_USE_WINDOW_FEATURE,
|
||||
CONF_USE_MOTION_FEATURE,
|
||||
CONF_USE_PRESENCE_FEATURE,
|
||||
CONF_USE_POWER_FEATURE,
|
||||
]
|
||||
+ CONF_PRESETS_VALUES
|
||||
+ CONF_PRESETS_AWAY_VALUES,
|
||||
@@ -101,6 +117,8 @@ CONF_FUNCTIONS = [
|
||||
PROPORTIONAL_FUNCTION_TPI,
|
||||
]
|
||||
|
||||
CONF_THERMOSTAT_TYPES = [CONF_THERMOSTAT_SWITCH, CONF_THERMOSTAT_CLIMATE]
|
||||
|
||||
SUPPORT_FLAGS = SUPPORT_TARGET_TEMPERATURE
|
||||
|
||||
SERVICE_SET_PRESENCE = "set_presence"
|
||||
|
||||
@@ -8,15 +8,25 @@
|
||||
"description": "Main mandatory attributes",
|
||||
"data": {
|
||||
"name": "Name",
|
||||
"heater_entity_id": "Heater entity id",
|
||||
"thermostat_type": "Thermostat type",
|
||||
"thermostat_over_switch": "Thermostat over a switch",
|
||||
"thermostat_over_climate": "Thermostat over another thermostat",
|
||||
"temperature_sensor_entity_id": "Temperature sensor entity id",
|
||||
"external_temperature_sensor_entity_id": "External temperature sensor entity id",
|
||||
"cycle_min": "Cycle duration (minutes)",
|
||||
"proportional_function": "Algorithm to use (TPI is the only one for now)",
|
||||
"temp_min": "Minimal temperature allowed",
|
||||
"temp_max": "Maximal temperature allowed"
|
||||
"temp_max": "Maximal temperature allowed",
|
||||
"use_window_feature": "Use window detection",
|
||||
"use_motion_feature": "Use motion detection",
|
||||
"use_power_feature": "Use power management",
|
||||
"use_presence_feature": "Use presence detection"
|
||||
}
|
||||
},
|
||||
"type": {
|
||||
"heater_entity_id": "Heater entity id",
|
||||
"proportional_function": "Algorithm to use (TPI is the only one for now)",
|
||||
"cycle_min": "Cycle duration (minutes)",
|
||||
"climate_entity_id": "Underlying thermostat entity id"
|
||||
},
|
||||
"tpi": {
|
||||
"title": "TPI",
|
||||
"description": "Time Proportional Integral attributes",
|
||||
@@ -97,15 +107,25 @@
|
||||
"description": "Main mandatory attributes",
|
||||
"data": {
|
||||
"name": "Name",
|
||||
"heater_entity_id": "Heater entity id",
|
||||
"thermostat_type": "Thermostat type",
|
||||
"thermostat_over_switch": "Thermostat over a switch",
|
||||
"thermostat_over_climate": "Thermostat over another thermostat",
|
||||
"temperature_sensor_entity_id": "Temperature sensor entity id",
|
||||
"external_temperature_sensor_entity_id": "External temperature sensor entity id",
|
||||
"cycle_min": "Cycle duration (minutes)",
|
||||
"proportional_function": "Algorithm to use (TPI is the only one for now)",
|
||||
"temp_min": "Minimal temperature allowed",
|
||||
"temp_max": "Maximal temperature allowed"
|
||||
"temp_max": "Maximal temperature allowed",
|
||||
"use_window_feature": "Use window detection",
|
||||
"use_motion_feature": "Use motion detection",
|
||||
"use_power_feature": "Use power management",
|
||||
"use_presence_feature": "Use presence detection"
|
||||
}
|
||||
},
|
||||
"type": {
|
||||
"heater_entity_id": "Heater entity id",
|
||||
"proportional_function": "Algorithm to use (TPI is the only one for now)",
|
||||
"cycle_min": "Cycle duration (minutes)",
|
||||
"climate_entity_id": "Underlying thermostat entity id"
|
||||
},
|
||||
"tpi": {
|
||||
"title": "TPI",
|
||||
"description": "Time Proportional Integral attributes",
|
||||
|
||||
@@ -8,15 +8,25 @@
|
||||
"description": "Main mandatory attributes",
|
||||
"data": {
|
||||
"name": "Name",
|
||||
"heater_entity_id": "Heater entity id",
|
||||
"thermostat_type": "Thermostat type",
|
||||
"thermostat_over_switch": "Thermostat over a switch",
|
||||
"thermostat_over_climate": "Thermostat over another thermostat",
|
||||
"temperature_sensor_entity_id": "Temperature sensor entity id",
|
||||
"external_temperature_sensor_entity_id": "External temperature sensor entity id",
|
||||
"cycle_min": "Cycle duration (minutes)",
|
||||
"proportional_function": "Algorithm to use (TPI is the only one for now)",
|
||||
"temp_min": "Minimal temperature allowed",
|
||||
"temp_max": "Maximal temperature allowed"
|
||||
"temp_max": "Maximal temperature allowed",
|
||||
"use_window_feature": "Use window detection",
|
||||
"use_motion_feature": "Use motion detection",
|
||||
"use_power_feature": "Use power management",
|
||||
"use_presence_feature": "Use presence detection"
|
||||
}
|
||||
},
|
||||
"type": {
|
||||
"heater_entity_id": "Heater entity id",
|
||||
"proportional_function": "Algorithm to use (TPI is the only one for now)",
|
||||
"cycle_min": "Cycle duration (minutes)",
|
||||
"climate_entity_id": "Underlying thermostat entity id"
|
||||
},
|
||||
"tpi": {
|
||||
"title": "TPI",
|
||||
"description": "Time Proportional Integral attributes",
|
||||
@@ -97,15 +107,25 @@
|
||||
"description": "Main mandatory attributes",
|
||||
"data": {
|
||||
"name": "Name",
|
||||
"heater_entity_id": "Heater entity id",
|
||||
"thermostat_type": "Thermostat type",
|
||||
"thermostat_over_switch": "Thermostat over a switch",
|
||||
"thermostat_over_climate": "Thermostat over another thermostat",
|
||||
"temperature_sensor_entity_id": "Temperature sensor entity id",
|
||||
"external_temperature_sensor_entity_id": "External temperature sensor entity id",
|
||||
"cycle_min": "Cycle duration (minutes)",
|
||||
"proportional_function": "Algorithm to use (TPI is the only one for now)",
|
||||
"temp_min": "Minimal temperature allowed",
|
||||
"temp_max": "Maximal temperature allowed"
|
||||
"temp_max": "Maximal temperature allowed",
|
||||
"use_window_feature": "Use window detection",
|
||||
"use_motion_feature": "Use motion detection",
|
||||
"use_power_feature": "Use power management",
|
||||
"use_presence_feature": "Use presence detection"
|
||||
}
|
||||
},
|
||||
"type": {
|
||||
"heater_entity_id": "Heater entity id",
|
||||
"proportional_function": "Algorithm to use (TPI is the only one for now)",
|
||||
"cycle_min": "Cycle duration (minutes)",
|
||||
"climate_entity_id": "Underlying thermostat entity id"
|
||||
},
|
||||
"tpi": {
|
||||
"title": "TPI",
|
||||
"description": "Time Proportional Integral attributes",
|
||||
|
||||
@@ -8,15 +8,25 @@
|
||||
"description": "Principaux attributs obligatoires",
|
||||
"data": {
|
||||
"name": "Nom",
|
||||
"heater_entity_id": "Radiateur entity id",
|
||||
"thermostat_over_switch": "Thermostat sur un switch",
|
||||
"thermostat_over_climate": "Thermostat sur un autre thermostat",
|
||||
"temperature_sensor_entity_id": "Température sensor entity id",
|
||||
"external_temperature_sensor_entity_id": "Temperature exterieure sensor entity id",
|
||||
"cycle_min": "Durée du cycle (minutes)",
|
||||
"proportional_function": "Algorithm à utiliser (Seul TPI est disponible pour l'instant)",
|
||||
"temp_min": "Température minimale permise",
|
||||
"temp_max": "Température maximale permise"
|
||||
"temp_max": "Température maximale permise",
|
||||
"use_window_feature": "Avec détection des ouvertures",
|
||||
"use_motion_feature": "Avec détection de mouvement",
|
||||
"use_power_feature": "Avec gestion de la puissance",
|
||||
"use_presence_feature": "Avec détection de présence"
|
||||
}
|
||||
},
|
||||
"type": {
|
||||
"heater_entity_id": "Radiateur entity id",
|
||||
"proportional_function": "Algorithm à utiliser (Seul TPI est disponible pour l'instant)",
|
||||
"cycle_min": "Durée du cycle (minutes)",
|
||||
"climate_entity_id": "Thermostat sous-jacent entity id"
|
||||
},
|
||||
"tpi": {
|
||||
"title": "TPI",
|
||||
"description": "Attributs de l'algo Time Proportional Integral",
|
||||
@@ -97,15 +107,25 @@
|
||||
"description": "Principaux attributs obligatoires",
|
||||
"data": {
|
||||
"name": "Nom",
|
||||
"heater_entity_id": "Radiateur entity id",
|
||||
"thermostat_over_switch": "Thermostat sur un switch",
|
||||
"thermostat_over_climate": "Thermostat sur un autre thermostat",
|
||||
"temperature_sensor_entity_id": "Température sensor entity id",
|
||||
"external_temperature_sensor_entity_id": "Temperature exterieure sensor entity id",
|
||||
"cycle_min": "Durée du cycle (minutes)",
|
||||
"proportional_function": "Algorithm à utiliser (Seul TPI est disponible pour l'instant)",
|
||||
"temp_min": "Température minimale permise",
|
||||
"temp_max": "Température maximale permise"
|
||||
"temp_max": "Température maximale permise",
|
||||
"use_window_feature": "Avec détection des ouvertures",
|
||||
"use_motion_feature": "Avec détection de mouvement",
|
||||
"use_power_feature": "Avec gestion de la puissance",
|
||||
"use_presence_feature": "Avec détection de présence"
|
||||
}
|
||||
},
|
||||
"type": {
|
||||
"heater_entity_id": "Radiateur entity id",
|
||||
"proportional_function": "Algorithm à utiliser (Seul TPI est disponible pour l'instant)",
|
||||
"cycle_min": "Durée du cycle (minutes)",
|
||||
"climate_entity_id": "Thermostat sous-jacent entity id"
|
||||
},
|
||||
"tpi": {
|
||||
"title": "TPI",
|
||||
"description": "Attributs de l'algo Time Proportional Integral",
|
||||
|
||||
Reference in New Issue
Block a user