Missing translations for preset mode Power and Security #46
Set preset internal #45 Notify (send event messages) when something important happens #43 Enhancing Security mode #42 Rename None preset to Manual #3
This commit is contained in:
@@ -20,7 +20,6 @@ from homeassistant.helpers.restore_state import RestoreEntity
|
|||||||
from homeassistant.config_entries import ConfigEntry
|
from homeassistant.config_entries import ConfigEntry
|
||||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||||
from homeassistant.helpers.entity_component import EntityComponent
|
from homeassistant.helpers.entity_component import EntityComponent
|
||||||
from homeassistant.util.unit_system import UnitOfTemperature
|
|
||||||
|
|
||||||
from homeassistant.helpers.event import (
|
from homeassistant.helpers.event import (
|
||||||
async_track_state_change_event,
|
async_track_state_change_event,
|
||||||
@@ -118,6 +117,9 @@ from .const import (
|
|||||||
PRESET_AWAY_SUFFIX,
|
PRESET_AWAY_SUFFIX,
|
||||||
CONF_SECURITY_DELAY_MIN,
|
CONF_SECURITY_DELAY_MIN,
|
||||||
CONF_SECURITY_MIN_ON_PERCENT,
|
CONF_SECURITY_MIN_ON_PERCENT,
|
||||||
|
CONF_SECURITY_DEFAULT_ON_PERCENT,
|
||||||
|
DEFAULT_SECURITY_MIN_ON_PERCENT,
|
||||||
|
DEFAULT_SECURITY_DEFAULT_ON_PERCENT,
|
||||||
CONF_MINIMAL_ACTIVATION_DELAY,
|
CONF_MINIMAL_ACTIVATION_DELAY,
|
||||||
CONF_TEMP_MAX,
|
CONF_TEMP_MAX,
|
||||||
CONF_TEMP_MIN,
|
CONF_TEMP_MIN,
|
||||||
@@ -127,6 +129,7 @@ from .const import (
|
|||||||
CONF_THERMOSTAT_CLIMATE,
|
CONF_THERMOSTAT_CLIMATE,
|
||||||
CONF_CLIMATE,
|
CONF_CLIMATE,
|
||||||
UnknownEntity,
|
UnknownEntity,
|
||||||
|
EventType,
|
||||||
)
|
)
|
||||||
|
|
||||||
from .prop_algorithm import PropAlgorithm
|
from .prop_algorithm import PropAlgorithm
|
||||||
@@ -183,7 +186,7 @@ class VersatileThermostat(ClimateEntity, RestoreEntity):
|
|||||||
# No more needed
|
# No more needed
|
||||||
# _registry: dict[str, object] = {}
|
# _registry: dict[str, object] = {}
|
||||||
|
|
||||||
def __init__(self, hass, unique_id, name, entry_infos) -> None:
|
def __init__(self, hass: HomeAssistant, unique_id, name, entry_infos) -> None:
|
||||||
"""Initialize the thermostat."""
|
"""Initialize the thermostat."""
|
||||||
|
|
||||||
super().__init__()
|
super().__init__()
|
||||||
@@ -218,9 +221,10 @@ class VersatileThermostat(ClimateEntity, RestoreEntity):
|
|||||||
self._presence_state = None
|
self._presence_state = None
|
||||||
self._overpowering_state = None
|
self._overpowering_state = None
|
||||||
self._should_relaunch_control_heating = None
|
self._should_relaunch_control_heating = None
|
||||||
self._security_delay_min = None
|
|
||||||
self._security_min_on_percent= None
|
|
||||||
|
|
||||||
|
self._security_delay_min = None
|
||||||
|
self._security_min_on_percent = None
|
||||||
|
self._security_default_on_percent = None
|
||||||
self._security_state = None
|
self._security_state = None
|
||||||
|
|
||||||
self._thermostat_type = None
|
self._thermostat_type = None
|
||||||
@@ -243,7 +247,7 @@ class VersatileThermostat(ClimateEntity, RestoreEntity):
|
|||||||
)
|
)
|
||||||
# convert entry_infos into usable attributes
|
# convert entry_infos into usable attributes
|
||||||
presets = {}
|
presets = {}
|
||||||
for (key, value) in CONF_PRESETS.items():
|
for key, value in CONF_PRESETS.items():
|
||||||
_LOGGER.debug("looking for key=%s, value=%s", key, value)
|
_LOGGER.debug("looking for key=%s, value=%s", key, value)
|
||||||
if value in entry_infos:
|
if value in entry_infos:
|
||||||
presets[key] = entry_infos.get(value)
|
presets[key] = entry_infos.get(value)
|
||||||
@@ -251,7 +255,7 @@ class VersatileThermostat(ClimateEntity, RestoreEntity):
|
|||||||
_LOGGER.debug("value %s not found in Entry", value)
|
_LOGGER.debug("value %s not found in Entry", value)
|
||||||
|
|
||||||
presets_away = {}
|
presets_away = {}
|
||||||
for (key, value) in CONF_PRESETS_AWAY.items():
|
for key, value in CONF_PRESETS_AWAY.items():
|
||||||
_LOGGER.debug("looking for key=%s, value=%s", key, value)
|
_LOGGER.debug("looking for key=%s, value=%s", key, value)
|
||||||
if value in entry_infos:
|
if value in entry_infos:
|
||||||
presets_away[key] = entry_infos.get(value)
|
presets_away[key] = entry_infos.get(value)
|
||||||
@@ -367,7 +371,14 @@ class VersatileThermostat(ClimateEntity, RestoreEntity):
|
|||||||
self._tpi_coef_ext = 0
|
self._tpi_coef_ext = 0
|
||||||
|
|
||||||
self._security_delay_min = entry_infos.get(CONF_SECURITY_DELAY_MIN)
|
self._security_delay_min = entry_infos.get(CONF_SECURITY_DELAY_MIN)
|
||||||
self._security_min_on_percent = entry_infos.get(CONF_SECURITY_MIN_ON_PERCENT)
|
self._security_min_on_percent = (
|
||||||
|
entry_infos.get(CONF_SECURITY_MIN_ON_PERCENT)
|
||||||
|
or DEFAULT_SECURITY_MIN_ON_PERCENT
|
||||||
|
)
|
||||||
|
self._security_default_on_percent = (
|
||||||
|
entry_infos.get(CONF_SECURITY_DEFAULT_ON_PERCENT)
|
||||||
|
or DEFAULT_SECURITY_DEFAULT_ON_PERCENT
|
||||||
|
)
|
||||||
self._minimal_activation_delay = entry_infos.get(CONF_MINIMAL_ACTIVATION_DELAY)
|
self._minimal_activation_delay = entry_infos.get(CONF_MINIMAL_ACTIVATION_DELAY)
|
||||||
self._last_temperature_mesure = datetime.now()
|
self._last_temperature_mesure = datetime.now()
|
||||||
self._last_ext_temperature_mesure = datetime.now()
|
self._last_ext_temperature_mesure = datetime.now()
|
||||||
@@ -732,7 +743,12 @@ class VersatileThermostat(ClimateEntity, RestoreEntity):
|
|||||||
float(old_state.attributes[ATTR_TEMPERATURE])
|
float(old_state.attributes[ATTR_TEMPERATURE])
|
||||||
)
|
)
|
||||||
|
|
||||||
if old_state.attributes.get(ATTR_PRESET_MODE) in self._attr_preset_modes:
|
old_preset_mode = old_state.attributes.get(ATTR_PRESET_MODE)
|
||||||
|
# Never restore a Power or Security preset
|
||||||
|
if (
|
||||||
|
old_preset_mode in self._attr_preset_modes
|
||||||
|
and old_preset_mode not in HIDDEN_PRESETS
|
||||||
|
):
|
||||||
self._attr_preset_mode = old_state.attributes.get(ATTR_PRESET_MODE)
|
self._attr_preset_mode = old_state.attributes.get(ATTR_PRESET_MODE)
|
||||||
self.save_preset_mode()
|
self.save_preset_mode()
|
||||||
|
|
||||||
@@ -756,6 +772,9 @@ class VersatileThermostat(ClimateEntity, RestoreEntity):
|
|||||||
if not self._hvac_mode:
|
if not self._hvac_mode:
|
||||||
self._hvac_mode = HVACMode.OFF
|
self._hvac_mode = HVACMode.OFF
|
||||||
|
|
||||||
|
self.send_event(EventType.PRESET_EVENT, {"preset": self._attr_preset_mode})
|
||||||
|
self.send_event(EventType.HVAC_MODE_EVENT, {"hvac_mode": self._hvac_mode})
|
||||||
|
|
||||||
_LOGGER.info(
|
_LOGGER.info(
|
||||||
"%s - restored state is target_temp=%.1f, preset_mode=%s, hvac_mode=%s",
|
"%s - restored state is target_temp=%.1f, preset_mode=%s, hvac_mode=%s",
|
||||||
self,
|
self,
|
||||||
@@ -1011,12 +1030,11 @@ class VersatileThermostat(ClimateEntity, RestoreEntity):
|
|||||||
_LOGGER.error("Unrecognized hvac mode: %s", hvac_mode)
|
_LOGGER.error("Unrecognized hvac mode: %s", hvac_mode)
|
||||||
return
|
return
|
||||||
# Ensure we update the current operation after changing the mode
|
# Ensure we update the current operation after changing the mode
|
||||||
self._last_temperature_mesure = (
|
self.reset_last_temperature_time()
|
||||||
self._last_ext_temperature_mesure
|
|
||||||
) = datetime.now()
|
|
||||||
|
|
||||||
self.update_custom_attributes()
|
self.update_custom_attributes()
|
||||||
self.async_write_ha_state()
|
self.async_write_ha_state()
|
||||||
|
self.send_event(EventType.HVAC_MODE_EVENT, {"hvac_mode": self._hvac_mode})
|
||||||
|
|
||||||
async def async_set_preset_mode(self, preset_mode):
|
async def async_set_preset_mode(self, preset_mode):
|
||||||
"""Set new preset mode."""
|
"""Set new preset mode."""
|
||||||
@@ -1052,11 +1070,18 @@ class VersatileThermostat(ClimateEntity, RestoreEntity):
|
|||||||
self.find_preset_temp(preset_mode)
|
self.find_preset_temp(preset_mode)
|
||||||
)
|
)
|
||||||
|
|
||||||
self._last_temperature_mesure = (
|
self.reset_last_temperature_time()
|
||||||
self._last_ext_temperature_mesure
|
|
||||||
) = datetime.now()
|
|
||||||
self.save_preset_mode()
|
self.save_preset_mode()
|
||||||
self.recalculate()
|
self.recalculate()
|
||||||
|
self.send_event(EventType.PRESET_EVENT, {"preset": self._attr_preset_mode})
|
||||||
|
|
||||||
|
def reset_last_temperature_time(self):
|
||||||
|
"""Reset to now the last temperature time if conditions are satisfied"""
|
||||||
|
if self._attr_preset_mode not in HIDDEN_PRESETS:
|
||||||
|
self._last_temperature_mesure = (
|
||||||
|
self._last_ext_temperature_mesure
|
||||||
|
) = datetime.now()
|
||||||
|
|
||||||
def find_preset_temp(self, preset_mode):
|
def find_preset_temp(self, preset_mode):
|
||||||
"""Find the right temperature of a preset considering the presence if configured"""
|
"""Find the right temperature of a preset considering the presence if configured"""
|
||||||
@@ -1623,6 +1648,15 @@ class VersatileThermostat(ClimateEntity, RestoreEntity):
|
|||||||
self.save_preset_mode()
|
self.save_preset_mode()
|
||||||
await self._async_underlying_entity_turn_off()
|
await self._async_underlying_entity_turn_off()
|
||||||
await self._async_set_preset_mode_internal(PRESET_POWER)
|
await self._async_set_preset_mode_internal(PRESET_POWER)
|
||||||
|
self.send_event(
|
||||||
|
EventType.POWER_EVENT,
|
||||||
|
{
|
||||||
|
"type": "start",
|
||||||
|
"current_power": self._current_power,
|
||||||
|
"device_power": self._device_power,
|
||||||
|
"current_power_max": self._current_power_max,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
# Check if we need to remove the POWER preset
|
# Check if we need to remove the POWER preset
|
||||||
if (
|
if (
|
||||||
@@ -1638,6 +1672,15 @@ class VersatileThermostat(ClimateEntity, RestoreEntity):
|
|||||||
if self._is_over_climate:
|
if self._is_over_climate:
|
||||||
await self.restore_hvac_mode()
|
await self.restore_hvac_mode()
|
||||||
await self.restore_preset_mode()
|
await self.restore_preset_mode()
|
||||||
|
self.send_event(
|
||||||
|
EventType.POWER_EVENT,
|
||||||
|
{
|
||||||
|
"type": "end",
|
||||||
|
"current_power": self._current_power,
|
||||||
|
"device_power": self._device_power,
|
||||||
|
"current_power_max": self._current_power_max,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
self._overpowering_state = ret
|
self._overpowering_state = ret
|
||||||
return self._overpowering_state
|
return self._overpowering_state
|
||||||
@@ -1649,12 +1692,6 @@ class VersatileThermostat(ClimateEntity, RestoreEntity):
|
|||||||
delta_ext_temp = (
|
delta_ext_temp = (
|
||||||
now - self._last_ext_temperature_mesure
|
now - self._last_ext_temperature_mesure
|
||||||
).total_seconds() / 60.0
|
).total_seconds() / 60.0
|
||||||
_LOGGER.debug(
|
|
||||||
"%s - checking security delta_temp=%.1f delta_ext_temp=%.1f",
|
|
||||||
self,
|
|
||||||
delta_temp,
|
|
||||||
delta_ext_temp,
|
|
||||||
)
|
|
||||||
|
|
||||||
temp_cond: bool = (
|
temp_cond: bool = (
|
||||||
delta_temp > self._security_delay_min
|
delta_temp > self._security_delay_min
|
||||||
@@ -1667,7 +1704,8 @@ class VersatileThermostat(ClimateEntity, RestoreEntity):
|
|||||||
switch_cond: bool = (
|
switch_cond: bool = (
|
||||||
not self._is_over_climate
|
not self._is_over_climate
|
||||||
and self._prop_algorithm is not None
|
and self._prop_algorithm is not None
|
||||||
and self._prop_algorithm.on_percent > self._security_min_on_percent
|
and self._prop_algorithm.calculated_on_percent
|
||||||
|
> self._security_min_on_percent
|
||||||
)
|
)
|
||||||
|
|
||||||
ret = False
|
ret = False
|
||||||
@@ -1683,6 +1721,16 @@ class VersatileThermostat(ClimateEntity, RestoreEntity):
|
|||||||
)
|
)
|
||||||
ret = True
|
ret = True
|
||||||
|
|
||||||
|
_LOGGER.debug(
|
||||||
|
"%s - checking security delta_temp=%.1f delta_ext_temp=%.1f temp_cond=%s climate_cond=%s switch_cond=%s",
|
||||||
|
self,
|
||||||
|
delta_temp,
|
||||||
|
delta_ext_temp,
|
||||||
|
temp_cond,
|
||||||
|
climate_cond,
|
||||||
|
switch_cond,
|
||||||
|
)
|
||||||
|
|
||||||
if temp_cond and switch_cond:
|
if temp_cond and switch_cond:
|
||||||
if not self._security_state:
|
if not self._security_state:
|
||||||
_LOGGER.warning(
|
_LOGGER.warning(
|
||||||
@@ -1696,12 +1744,40 @@ class VersatileThermostat(ClimateEntity, RestoreEntity):
|
|||||||
)
|
)
|
||||||
ret = True
|
ret = True
|
||||||
|
|
||||||
|
if not self._security_state and temp_cond:
|
||||||
|
self.send_event(
|
||||||
|
EventType.TEMPERATURE_EVENT,
|
||||||
|
{
|
||||||
|
"last_temperature_mesure": self._last_temperature_mesure.isoformat(),
|
||||||
|
"last_ext_temperature_mesure": self._last_ext_temperature_mesure.isoformat(),
|
||||||
|
"current_temp": self._cur_temp,
|
||||||
|
"current_ext_temp": self._cur_ext_temp,
|
||||||
|
"target_temp": self.target_temperature,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
if not self._security_state and ret:
|
if not self._security_state and ret:
|
||||||
self._security_state = ret
|
self._security_state = ret
|
||||||
self.save_hvac_mode()
|
self.save_hvac_mode()
|
||||||
self.save_preset_mode()
|
self.save_preset_mode()
|
||||||
await self._async_set_preset_mode_internal(PRESET_SECURITY)
|
await self._async_set_preset_mode_internal(PRESET_SECURITY)
|
||||||
await self.async_set_hvac_mode(HVACMode.OFF)
|
# Turn off the underlying climate or heater if security default on_percent is 0
|
||||||
|
if self._is_over_climate or self._security_default_on_percent <= 0.0:
|
||||||
|
await self.async_set_hvac_mode(HVACMode.OFF)
|
||||||
|
if self._prop_algorithm:
|
||||||
|
self._prop_algorithm.set_security(self._security_default_on_percent)
|
||||||
|
|
||||||
|
self.send_event(
|
||||||
|
EventType.SECURITY_EVENT,
|
||||||
|
{
|
||||||
|
"type": "start",
|
||||||
|
"last_temperature_mesure": self._last_temperature_mesure.isoformat(),
|
||||||
|
"last_ext_temperature_mesure": self._last_ext_temperature_mesure.isoformat(),
|
||||||
|
"current_temp": self._cur_temp,
|
||||||
|
"current_ext_temp": self._cur_ext_temp,
|
||||||
|
"target_temp": self.target_temperature,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
if (
|
if (
|
||||||
self._security_state
|
self._security_state
|
||||||
@@ -1715,15 +1791,30 @@ class VersatileThermostat(ClimateEntity, RestoreEntity):
|
|||||||
self._saved_preset_mode,
|
self._saved_preset_mode,
|
||||||
)
|
)
|
||||||
self._security_state = ret
|
self._security_state = ret
|
||||||
await self.restore_hvac_mode()
|
# Restore hvac_mode if previously saved
|
||||||
|
if self._is_over_climate or self._security_default_on_percent <= 0.0:
|
||||||
|
await self.restore_hvac_mode()
|
||||||
await self.restore_preset_mode()
|
await self.restore_preset_mode()
|
||||||
|
if self._prop_algorithm:
|
||||||
|
self._prop_algorithm.unset_security()
|
||||||
|
self.send_event(
|
||||||
|
EventType.SECURITY_EVENT,
|
||||||
|
{
|
||||||
|
"type": "end",
|
||||||
|
"last_temperature_mesure": self._last_temperature_mesure.isoformat(),
|
||||||
|
"last_ext_temperature_mesure": self._last_ext_temperature_mesure.isoformat(),
|
||||||
|
"current_temp": self._cur_temp,
|
||||||
|
"current_ext_temp": self._cur_ext_temp,
|
||||||
|
"target_temp": self.target_temperature,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
async def _async_control_heating(self, force=False, _=None):
|
async def _async_control_heating(self, force=False, _=None):
|
||||||
"""The main function used to run the calculation at each cycle"""
|
"""The main function used to run the calculation at each cycle"""
|
||||||
|
|
||||||
_LOGGER.info(
|
_LOGGER.debug(
|
||||||
"%s - Checking new cycle. hvac_mode=%s, security_state=%s, preset_mode=%s",
|
"%s - Checking new cycle. hvac_mode=%s, security_state=%s, preset_mode=%s",
|
||||||
self,
|
self,
|
||||||
self._hvac_mode,
|
self._hvac_mode,
|
||||||
@@ -1738,8 +1829,8 @@ class VersatileThermostat(ClimateEntity, RestoreEntity):
|
|||||||
return
|
return
|
||||||
|
|
||||||
security: bool = await self.check_security()
|
security: bool = await self.check_security()
|
||||||
if security:
|
if security and self._is_over_climate:
|
||||||
_LOGGER.debug("%s - End of cycle (security)", self)
|
_LOGGER.debug("%s - End of cycle (security and over climate)", self)
|
||||||
return
|
return
|
||||||
|
|
||||||
# Stop here if we are off
|
# Stop here if we are off
|
||||||
@@ -1752,7 +1843,8 @@ class VersatileThermostat(ClimateEntity, RestoreEntity):
|
|||||||
if not self._is_over_climate:
|
if not self._is_over_climate:
|
||||||
on_time_sec: int = self._prop_algorithm.on_time_sec
|
on_time_sec: int = self._prop_algorithm.on_time_sec
|
||||||
off_time_sec: int = self._prop_algorithm.off_time_sec
|
off_time_sec: int = self._prop_algorithm.off_time_sec
|
||||||
_LOGGER.info(
|
|
||||||
|
_LOGGER.debug(
|
||||||
"%s - Checking new cycle. on_time_sec=%.0f, off_time_sec=%.0f, security_state=%s, preset_mode=%s",
|
"%s - Checking new cycle. on_time_sec=%.0f, off_time_sec=%.0f, security_state=%s, preset_mode=%s",
|
||||||
self,
|
self,
|
||||||
on_time_sec,
|
on_time_sec,
|
||||||
@@ -1793,13 +1885,12 @@ class VersatileThermostat(ClimateEntity, RestoreEntity):
|
|||||||
return
|
return
|
||||||
|
|
||||||
if on:
|
if on:
|
||||||
security = (
|
if await self.check_overpowering():
|
||||||
await self.check_security()
|
|
||||||
or await self.check_overpowering()
|
|
||||||
)
|
|
||||||
if security:
|
|
||||||
_LOGGER.debug("%s - End of cycle (3)", self)
|
_LOGGER.debug("%s - End of cycle (3)", self)
|
||||||
return
|
return
|
||||||
|
# Security mode could have change the on_time percent
|
||||||
|
await self.check_security()
|
||||||
|
time = self._prop_algorithm.on_time_sec
|
||||||
|
|
||||||
action_label = "start" if on else "stop"
|
action_label = "start" if on else "stop"
|
||||||
if self._should_relaunch_control_heating:
|
if self._should_relaunch_control_heating:
|
||||||
@@ -1814,7 +1905,7 @@ class VersatileThermostat(ClimateEntity, RestoreEntity):
|
|||||||
|
|
||||||
if time > 0:
|
if time > 0:
|
||||||
_LOGGER.info(
|
_LOGGER.info(
|
||||||
"%s - !!! %s heating for %d min %d sec",
|
"%s - %s heating for %d min %d sec",
|
||||||
self,
|
self,
|
||||||
action_label,
|
action_label,
|
||||||
time // 60,
|
time // 60,
|
||||||
@@ -1910,6 +2001,7 @@ class VersatileThermostat(ClimateEntity, RestoreEntity):
|
|||||||
"presence_state": self._presence_state,
|
"presence_state": self._presence_state,
|
||||||
"security_delay_min": self._security_delay_min,
|
"security_delay_min": self._security_delay_min,
|
||||||
"security_min_on_percent": self._security_min_on_percent,
|
"security_min_on_percent": self._security_min_on_percent,
|
||||||
|
"security_default_on_percent": self._security_default_on_percent,
|
||||||
"last_temperature_datetime": self._last_temperature_mesure.isoformat(),
|
"last_temperature_datetime": self._last_temperature_mesure.isoformat(),
|
||||||
"last_ext_temperature_datetime": self._last_ext_temperature_mesure.isoformat(),
|
"last_ext_temperature_datetime": self._last_ext_temperature_mesure.isoformat(),
|
||||||
"security_state": self._security_state,
|
"security_state": self._security_state,
|
||||||
@@ -1999,3 +2091,11 @@ class VersatileThermostat(ClimateEntity, RestoreEntity):
|
|||||||
if self._attr_preset_mode == preset:
|
if self._attr_preset_mode == preset:
|
||||||
await self._async_set_preset_mode_internal(preset, force=True)
|
await self._async_set_preset_mode_internal(preset, force=True)
|
||||||
await self._async_control_heating(force=True)
|
await self._async_control_heating(force=True)
|
||||||
|
|
||||||
|
def send_event(self, event_type: EventType, data: dict):
|
||||||
|
"""Send an event"""
|
||||||
|
_LOGGER.info("%s - Sending event %s with data: %s", self, event_type, data)
|
||||||
|
data["entity_id"] = self.entity_id
|
||||||
|
data["name"] = self.name
|
||||||
|
data["state_attributes"] = self.state_attributes
|
||||||
|
self._hass.bus.fire(event_type.value, data)
|
||||||
|
|||||||
@@ -64,6 +64,9 @@ from .const import (
|
|||||||
PROPORTIONAL_FUNCTION_TPI,
|
PROPORTIONAL_FUNCTION_TPI,
|
||||||
CONF_SECURITY_DELAY_MIN,
|
CONF_SECURITY_DELAY_MIN,
|
||||||
CONF_SECURITY_MIN_ON_PERCENT,
|
CONF_SECURITY_MIN_ON_PERCENT,
|
||||||
|
CONF_SECURITY_DEFAULT_ON_PERCENT,
|
||||||
|
DEFAULT_SECURITY_MIN_ON_PERCENT,
|
||||||
|
DEFAULT_SECURITY_DEFAULT_ON_PERCENT,
|
||||||
CONF_MINIMAL_ACTIVATION_DELAY,
|
CONF_MINIMAL_ACTIVATION_DELAY,
|
||||||
CONF_TEMP_MAX,
|
CONF_TEMP_MAX,
|
||||||
CONF_TEMP_MIN,
|
CONF_TEMP_MIN,
|
||||||
@@ -177,53 +180,16 @@ class VersatileThermostatBaseConfigFlow(FlowHandler):
|
|||||||
is_empty or self._infos.get(CONF_PRESENCE_SENSOR) is not None
|
is_empty or self._infos.get(CONF_PRESENCE_SENSOR) is not None
|
||||||
)
|
)
|
||||||
|
|
||||||
# self.hass = async_get_hass()
|
|
||||||
# ent_reg = async_get(hass=self.hass)
|
|
||||||
|
|
||||||
# climates = []
|
|
||||||
# switches = []
|
|
||||||
# temp_sensors = []
|
|
||||||
# power_sensors = []
|
|
||||||
# window_sensors = []
|
|
||||||
# presence_sensors = []
|
|
||||||
#
|
|
||||||
# k: str
|
|
||||||
# for k in ent_reg.entities:
|
|
||||||
# v: RegistryEntry = ent_reg.entities[k]
|
|
||||||
# _LOGGER.debug("Looking entity: %s", k)
|
|
||||||
# # if k.startswith(CLIMATE_DOMAIN) and (
|
|
||||||
# # infos is None or k != infos.get("entity_id")
|
|
||||||
# # ):
|
|
||||||
# # _LOGGER.debug("Climate !")
|
|
||||||
# # climates.append(k)
|
|
||||||
# if k.startswith(SWITCH_DOMAIN) or k.startswith(INPUT_BOOLEAN_DOMAIN):
|
|
||||||
# _LOGGER.debug("Switch !")
|
|
||||||
# switches.append(k)
|
|
||||||
# elif is_temperature_sensor(v):
|
|
||||||
# _LOGGER.debug("Temperature sensor !")
|
|
||||||
# temp_sensors.append(k)
|
|
||||||
# elif is_power_sensor(v):
|
|
||||||
# _LOGGER.debug("Power sensor !")
|
|
||||||
# power_sensors.append(k)
|
|
||||||
# elif k.startswith(PERSON_DOMAIN):
|
|
||||||
# _LOGGER.debug("Presence sensor !")
|
|
||||||
# presence_sensors.append(k)
|
|
||||||
#
|
|
||||||
# # window sensor and presence
|
|
||||||
# if k.startswith(INPUT_BOOLEAN_DOMAIN) or k.startswith(BINARY_SENSOR_DOMAIN):
|
|
||||||
# _LOGGER.debug("Window or presence sensor !")
|
|
||||||
# window_sensors.append(k)
|
|
||||||
# presence_sensors.append(k)
|
|
||||||
#
|
|
||||||
# # Special case for climates which are not in EntityRegistry
|
|
||||||
# climates = self.find_all_climates()
|
|
||||||
|
|
||||||
self.STEP_USER_DATA_SCHEMA = vol.Schema(
|
self.STEP_USER_DATA_SCHEMA = vol.Schema(
|
||||||
{
|
{
|
||||||
vol.Required(CONF_NAME): cv.string,
|
vol.Required(CONF_NAME): cv.string,
|
||||||
vol.Required(
|
vol.Required(
|
||||||
CONF_THERMOSTAT_TYPE, default=CONF_THERMOSTAT_SWITCH
|
CONF_THERMOSTAT_TYPE, default=CONF_THERMOSTAT_SWITCH
|
||||||
): vol.In(CONF_THERMOSTAT_TYPES),
|
): selector.SelectSelector(
|
||||||
|
selector.SelectSelectorConfig(
|
||||||
|
options=CONF_THERMOSTAT_TYPES, translation_key="thermostat_type"
|
||||||
|
)
|
||||||
|
),
|
||||||
vol.Required(CONF_TEMP_SENSOR): selector.EntitySelector(
|
vol.Required(CONF_TEMP_SENSOR): selector.EntitySelector(
|
||||||
selector.EntitySelectorConfig(
|
selector.EntitySelectorConfig(
|
||||||
domain=[SENSOR_DOMAIN, INPUT_NUMBER_DOMAIN]
|
domain=[SENSOR_DOMAIN, INPUT_NUMBER_DOMAIN]
|
||||||
@@ -350,9 +316,18 @@ class VersatileThermostatBaseConfigFlow(FlowHandler):
|
|||||||
|
|
||||||
self.STEP_ADVANCED_DATA_SCHEMA = vol.Schema(
|
self.STEP_ADVANCED_DATA_SCHEMA = vol.Schema(
|
||||||
{
|
{
|
||||||
vol.Required(CONF_MINIMAL_ACTIVATION_DELAY, default=10): cv.positive_int,
|
vol.Required(
|
||||||
|
CONF_MINIMAL_ACTIVATION_DELAY, default=10
|
||||||
|
): cv.positive_int,
|
||||||
vol.Required(CONF_SECURITY_DELAY_MIN, default=60): cv.positive_int,
|
vol.Required(CONF_SECURITY_DELAY_MIN, default=60): cv.positive_int,
|
||||||
vol.Required(CONF_SECURITY_MIN_ON_PERCENT, default=0.75): vol.Coerce(float),
|
vol.Required(
|
||||||
|
CONF_SECURITY_MIN_ON_PERCENT,
|
||||||
|
default=DEFAULT_SECURITY_MIN_ON_PERCENT,
|
||||||
|
): vol.Coerce(float),
|
||||||
|
vol.Required(
|
||||||
|
CONF_SECURITY_DEFAULT_ON_PERCENT,
|
||||||
|
default=DEFAULT_SECURITY_DEFAULT_ON_PERCENT,
|
||||||
|
): vol.Coerce(float),
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
"""Constants for the Versatile Thermostat integration."""
|
"""Constants for the Versatile Thermostat integration."""
|
||||||
|
|
||||||
|
from enum import Enum
|
||||||
from homeassistant.const import CONF_NAME
|
from homeassistant.const import CONF_NAME
|
||||||
from homeassistant.components.climate.const import (
|
from homeassistant.components.climate.const import (
|
||||||
# PRESET_ACTIVITY,
|
# PRESET_ACTIVITY,
|
||||||
@@ -45,6 +46,7 @@ CONF_TEMP_MIN = "temp_min"
|
|||||||
CONF_TEMP_MAX = "temp_max"
|
CONF_TEMP_MAX = "temp_max"
|
||||||
CONF_SECURITY_DELAY_MIN = "security_delay_min"
|
CONF_SECURITY_DELAY_MIN = "security_delay_min"
|
||||||
CONF_SECURITY_MIN_ON_PERCENT = "security_min_on_percent"
|
CONF_SECURITY_MIN_ON_PERCENT = "security_min_on_percent"
|
||||||
|
CONF_SECURITY_DEFAULT_ON_PERCENT = "security_default_on_percent"
|
||||||
CONF_THERMOSTAT_TYPE = "thermostat_type"
|
CONF_THERMOSTAT_TYPE = "thermostat_type"
|
||||||
CONF_THERMOSTAT_SWITCH = "thermostat_over_switch"
|
CONF_THERMOSTAT_SWITCH = "thermostat_over_switch"
|
||||||
CONF_THERMOSTAT_CLIMATE = "thermostat_over_climate"
|
CONF_THERMOSTAT_CLIMATE = "thermostat_over_climate"
|
||||||
@@ -104,6 +106,7 @@ ALL_CONF = (
|
|||||||
CONF_TEMP_MAX,
|
CONF_TEMP_MAX,
|
||||||
CONF_SECURITY_DELAY_MIN,
|
CONF_SECURITY_DELAY_MIN,
|
||||||
CONF_SECURITY_MIN_ON_PERCENT,
|
CONF_SECURITY_MIN_ON_PERCENT,
|
||||||
|
CONF_SECURITY_DEFAULT_ON_PERCENT,
|
||||||
CONF_THERMOSTAT_TYPE,
|
CONF_THERMOSTAT_TYPE,
|
||||||
CONF_THERMOSTAT_SWITCH,
|
CONF_THERMOSTAT_SWITCH,
|
||||||
CONF_THERMOSTAT_CLIMATE,
|
CONF_THERMOSTAT_CLIMATE,
|
||||||
@@ -128,6 +131,17 @@ SUPPORT_FLAGS = SUPPORT_TARGET_TEMPERATURE
|
|||||||
SERVICE_SET_PRESENCE = "set_presence"
|
SERVICE_SET_PRESENCE = "set_presence"
|
||||||
SERVICE_SET_PRESET_TEMPERATURE = "set_preset_temperature"
|
SERVICE_SET_PRESET_TEMPERATURE = "set_preset_temperature"
|
||||||
|
|
||||||
|
DEFAULT_SECURITY_MIN_ON_PERCENT = 0.5
|
||||||
|
DEFAULT_SECURITY_DEFAULT_ON_PERCENT = 0.1
|
||||||
|
|
||||||
|
|
||||||
|
class EventType(Enum):
|
||||||
|
SECURITY_EVENT: str = "versatile_thermostat_security_event"
|
||||||
|
POWER_EVENT: str = "versatile_thermostat_power_event"
|
||||||
|
TEMPERATURE_EVENT: str = "versatile_thermostat_temperature_event"
|
||||||
|
HVAC_MODE_EVENT: str = "versatile_thermostat_hvac_mode_event"
|
||||||
|
PRESET_EVENT: str = "versatile_thermostat_preset_event"
|
||||||
|
|
||||||
|
|
||||||
class UnknownEntity(HomeAssistantError):
|
class UnknownEntity(HomeAssistantError):
|
||||||
"""Error to indicate there is an unknown entity_id given."""
|
"""Error to indicate there is an unknown entity_id given."""
|
||||||
|
|||||||
@@ -37,8 +37,11 @@ class PropAlgorithm:
|
|||||||
self._cycle_min = cycle_min
|
self._cycle_min = cycle_min
|
||||||
self._minimal_activation_delay = minimal_activation_delay
|
self._minimal_activation_delay = minimal_activation_delay
|
||||||
self._on_percent = 0
|
self._on_percent = 0
|
||||||
|
self._calculated_on_percent = 0
|
||||||
self._on_time_sec = 0
|
self._on_time_sec = 0
|
||||||
self._off_time_sec = self._cycle_min * 60
|
self._off_time_sec = self._cycle_min * 60
|
||||||
|
self._security = False
|
||||||
|
self._default_on_percent = 0
|
||||||
|
|
||||||
def calculate(
|
def calculate(
|
||||||
self, target_temp: float, current_temp: float, ext_current_temp: float
|
self, target_temp: float, current_temp: float, ext_current_temp: float
|
||||||
@@ -48,7 +51,7 @@ class PropAlgorithm:
|
|||||||
_LOGGER.warning(
|
_LOGGER.warning(
|
||||||
"Proportional algorithm: calculation is not possible cause target_temp or current_temp is null. Heating will be disabled" # pylint: disable=line-too-long
|
"Proportional algorithm: calculation is not possible cause target_temp or current_temp is null. Heating will be disabled" # pylint: disable=line-too-long
|
||||||
)
|
)
|
||||||
self._on_percent = 0
|
self._calculated_on_percent = 0
|
||||||
else:
|
else:
|
||||||
delta_temp = target_temp - current_temp
|
delta_temp = target_temp - current_temp
|
||||||
delta_ext_temp = (
|
delta_ext_temp = (
|
||||||
@@ -56,7 +59,7 @@ class PropAlgorithm:
|
|||||||
)
|
)
|
||||||
|
|
||||||
if self._function == PROPORTIONAL_FUNCTION_TPI:
|
if self._function == PROPORTIONAL_FUNCTION_TPI:
|
||||||
self._on_percent = (
|
self._calculated_on_percent = (
|
||||||
self._tpi_coef_int * delta_temp
|
self._tpi_coef_int * delta_temp
|
||||||
+ self._tpi_coef_ext * delta_ext_temp
|
+ self._tpi_coef_ext * delta_ext_temp
|
||||||
)
|
)
|
||||||
@@ -65,7 +68,35 @@ class PropAlgorithm:
|
|||||||
"Proportional algorithm: unknown %s function. Heating will be disabled",
|
"Proportional algorithm: unknown %s function. Heating will be disabled",
|
||||||
self._function,
|
self._function,
|
||||||
)
|
)
|
||||||
self._on_percent = 0
|
self._calculated_on_percent = 0
|
||||||
|
|
||||||
|
self._calculate_internal()
|
||||||
|
|
||||||
|
_LOGGER.debug(
|
||||||
|
"heating percent calculated for current_temp %.1f, ext_current_temp %.1f and target_temp %.1f is %.2f, on_time is %d (sec), off_time is %d (sec)", # pylint: disable=line-too-long
|
||||||
|
current_temp if current_temp else -9999.0,
|
||||||
|
ext_current_temp if ext_current_temp else -9999.0,
|
||||||
|
target_temp if target_temp else -9999.0,
|
||||||
|
self._calculated_on_percent,
|
||||||
|
self.on_time_sec,
|
||||||
|
self.off_time_sec,
|
||||||
|
)
|
||||||
|
|
||||||
|
def _calculate_internal(self):
|
||||||
|
"""Finish the calculation to get the on_percent in seconds"""
|
||||||
|
|
||||||
|
if self._security:
|
||||||
|
_LOGGER.debug(
|
||||||
|
"Security is On using the default_on_percent %f",
|
||||||
|
self._default_on_percent,
|
||||||
|
)
|
||||||
|
self._on_percent = self._default_on_percent
|
||||||
|
else:
|
||||||
|
_LOGGER.debug(
|
||||||
|
"Security is Off using the calculated_on_percent %f",
|
||||||
|
self._calculated_on_percent,
|
||||||
|
)
|
||||||
|
self._on_percent = self._calculated_on_percent
|
||||||
|
|
||||||
# calculated on_time duration in seconds
|
# calculated on_time duration in seconds
|
||||||
if self._on_percent > 1:
|
if self._on_percent > 1:
|
||||||
@@ -92,21 +123,31 @@ class PropAlgorithm:
|
|||||||
|
|
||||||
self._off_time_sec = self._cycle_min * 60 - self._on_time_sec
|
self._off_time_sec = self._cycle_min * 60 - self._on_time_sec
|
||||||
|
|
||||||
_LOGGER.debug(
|
def set_security(self, default_on_percent: float):
|
||||||
"heating percent calculated for current_temp %.1f, ext_current_temp %.1f and target_temp %.1f is %.2f, on_time is %d (sec), off_time is %d (sec)", # pylint: disable=line-too-long
|
"""Set a default value for on_percent (used for security mode)"""
|
||||||
current_temp if current_temp else -9999.0,
|
self._security = True
|
||||||
ext_current_temp if ext_current_temp else -9999.0,
|
self._default_on_percent = default_on_percent
|
||||||
target_temp if target_temp else -9999.0,
|
self._calculate_internal()
|
||||||
self._on_percent,
|
|
||||||
self.on_time_sec,
|
def unset_security(self):
|
||||||
self.off_time_sec,
|
"""Unset the security mode"""
|
||||||
)
|
self._security = False
|
||||||
|
self._calculate_internal()
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def on_percent(self) -> float:
|
def on_percent(self) -> float:
|
||||||
"""Returns the percentage the heater must be ON (1 means the heater will be always on, 0 never on)""" # pylint: disable=line-too-long
|
"""Returns the percentage the heater must be ON
|
||||||
|
In security mode this value is overriden with the _default_on_percent
|
||||||
|
(1 means the heater will be always on, 0 never on)""" # pylint: disable=line-too-long
|
||||||
return round(self._on_percent, 2)
|
return round(self._on_percent, 2)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def calculated_on_percent(self) -> float:
|
||||||
|
"""Returns the calculated percentage the heater must be ON
|
||||||
|
Calculated means NOT overriden even in security mode
|
||||||
|
(1 means the heater will be always on, 0 never on)""" # pylint: disable=line-too-long
|
||||||
|
return round(self._calculated_on_percent, 2)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def on_time_sec(self) -> int:
|
def on_time_sec(self) -> int:
|
||||||
"""Returns the calculated time in sec the heater must be ON"""
|
"""Returns the calculated time in sec the heater must be ON"""
|
||||||
|
|||||||
@@ -90,7 +90,8 @@
|
|||||||
"data": {
|
"data": {
|
||||||
"minimal_activation_delay": "Delay in secondes under which the equipment will not be activated",
|
"minimal_activation_delay": "Delay in secondes under which the equipment will not be activated",
|
||||||
"security_delay_min": "Maximum allowed delay in minutes between two temperature mesures. Above this delay, the thermostat will turn to a sceurity off state",
|
"security_delay_min": "Maximum allowed delay in minutes between two temperature mesures. Above this delay, the thermostat will turn to a sceurity off state",
|
||||||
"security_min_on_percent": "Minimal On-Percent value for security preset activation. Below this amount of on_percent the thermostat won't go into security preset"
|
"security_min_on_percent": "Minimal heating percent value for security preset activation. Below this amount of on_percent the thermostat won't go into security preset",
|
||||||
|
"security_default_on_percent": "The default heating percent value in security preset. Set to 0 to switch off heater in security present"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -192,7 +193,8 @@
|
|||||||
"data": {
|
"data": {
|
||||||
"minimal_activation_delay": "Delay in secondes under which the equipment will not be activated",
|
"minimal_activation_delay": "Delay in secondes under which the equipment will not be activated",
|
||||||
"security_delay_min": "Maximum allowed delay in minutes between two temperature mesures. Above this delay, the thermostat will turn to a security off state",
|
"security_delay_min": "Maximum allowed delay in minutes between two temperature mesures. Above this delay, the thermostat will turn to a security off state",
|
||||||
"security_min_on_percent": "Minimal On-Percent value for security preset activation. Below this amount of on_percent the thermostat won't go into security preset"
|
"security_min_on_percent": "Minimal heating percent value for security preset activation. Below this amount of on_percent the thermostat won't go into security preset",
|
||||||
|
"security_default_on_percent": "The default heating percent value in security preset. Set to 0 to switch off heater in security present"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -215,10 +217,13 @@
|
|||||||
"entity": {
|
"entity": {
|
||||||
"climate": {
|
"climate": {
|
||||||
"versatile_thermostat": {
|
"versatile_thermostat": {
|
||||||
"states_attributes": {
|
"state_attributes": {
|
||||||
"preset_mode": {
|
"preset_mode": {
|
||||||
"power": "Shedding",
|
"state": {
|
||||||
"security": "Security"
|
"power": "Shedding",
|
||||||
|
"security": "Security",
|
||||||
|
"none": "Manual"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -90,7 +90,8 @@
|
|||||||
"data": {
|
"data": {
|
||||||
"minimal_activation_delay": "Delay in secondes under which the equipment will not be activated",
|
"minimal_activation_delay": "Delay in secondes under which the equipment will not be activated",
|
||||||
"security_delay_min": "Maximum allowed delay in minutes between two temperature mesures. Above this delay, the thermostat will turn to a sceurity off state",
|
"security_delay_min": "Maximum allowed delay in minutes between two temperature mesures. Above this delay, the thermostat will turn to a sceurity off state",
|
||||||
"security_min_on_percent": "Minimal On-Percent value for security preset activation. Below this amount of on_percent the thermostat won't go into security preset"
|
"security_min_on_percent": "Minimal heating percent value for security preset activation. Below this amount of on_percent the thermostat won't go into security preset",
|
||||||
|
"security_default_on_percent": "The default heating percent value in security preset. Set to 0 to switch off heater in security present"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -192,7 +193,8 @@
|
|||||||
"data": {
|
"data": {
|
||||||
"minimal_activation_delay": "Delay in secondes under which the equipment will not be activated",
|
"minimal_activation_delay": "Delay in secondes under which the equipment will not be activated",
|
||||||
"security_delay_min": "Maximum allowed delay in minutes between two temperature mesures. Above this delay, the thermostat will turn to a sceurity off state",
|
"security_delay_min": "Maximum allowed delay in minutes between two temperature mesures. Above this delay, the thermostat will turn to a sceurity off state",
|
||||||
"security_min_on_percent": "Minimal On-Percent value for security preset activation. Below this amount of on_percent the thermostat won't go into security preset"
|
"security_min_on_percent": "Minimal heating percent value for security preset activation. Below this amount of on_percent the thermostat won't go into security preset",
|
||||||
|
"security_default_on_percent": "The default heating percent value in security preset. Set to 0 to switch off heater in security present"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -215,10 +217,13 @@
|
|||||||
"entity": {
|
"entity": {
|
||||||
"climate": {
|
"climate": {
|
||||||
"versatile_thermostat": {
|
"versatile_thermostat": {
|
||||||
"states_attributes": {
|
"state_attributes": {
|
||||||
"preset_mode": {
|
"preset_mode": {
|
||||||
"power": "Shedding",
|
"state": {
|
||||||
"security": "Security"
|
"power": "Shedding",
|
||||||
|
"security": "Security",
|
||||||
|
"none": "Manual"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -89,7 +89,8 @@
|
|||||||
"data": {
|
"data": {
|
||||||
"minimal_activation_delay": "Délai en secondes en-dessous duquel l'équipement ne sera pas activé",
|
"minimal_activation_delay": "Délai en secondes en-dessous duquel l'équipement ne sera pas activé",
|
||||||
"security_delay_min": "Délai maximal autorisé en minutes entre 2 mesures de températures. Au-dessus de ce délai, le thermostat se mettra en position éteinte de sécurité",
|
"security_delay_min": "Délai maximal autorisé en minutes entre 2 mesures de températures. Au-dessus de ce délai, le thermostat se mettra en position éteinte de sécurité",
|
||||||
"security_min_on_percent": "Seuil de chauffage en-dessous duquel le préréglage sécurité ne sera jamais activé."
|
"security_min_on_percent": "Seuil minimal de pourcentage de chauffage en-dessous duquel le préréglage sécurité ne sera jamais activé",
|
||||||
|
"security_default_on_percent": "Valeur par défaut pour le pourcentage de chauffage en mode sécurité. Mettre 0 pour éteindre le radiateur en mode sécurité"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -192,7 +193,8 @@
|
|||||||
"data": {
|
"data": {
|
||||||
"minimal_activation_delay": "Délai en seondes en-dessous duquel l'équipement ne sera pas activé",
|
"minimal_activation_delay": "Délai en seondes en-dessous duquel l'équipement ne sera pas activé",
|
||||||
"security_delay_min": "Délai maximal autorisé en minutes entre 2 mesures de températures. Au-dessus de ce délai, le thermostat se mettra en position éteinte de sécurité",
|
"security_delay_min": "Délai maximal autorisé en minutes entre 2 mesures de températures. Au-dessus de ce délai, le thermostat se mettra en position éteinte de sécurité",
|
||||||
"security_min_on_percent": "Seuil de chauffage en-dessous duquel le préréglage sécurité ne sera jamais activé."
|
"security_min_on_percent": "Seuil minimal de pourcentage de chauffage en-dessous duquel le préréglage sécurité ne sera jamais activé",
|
||||||
|
"security_default_on_percent": "Valeur par défaut pour le pourcentage de chauffage en mode sécurité. Mettre 0 pour éteindre le radiateur en mode sécurité"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -215,10 +217,13 @@
|
|||||||
"entity": {
|
"entity": {
|
||||||
"climate": {
|
"climate": {
|
||||||
"versatile_thermostat": {
|
"versatile_thermostat": {
|
||||||
"states_attributes": {
|
"state_attributes": {
|
||||||
"preset_mode": {
|
"preset_mode": {
|
||||||
"power": "Délestage",
|
"state": {
|
||||||
"security": "Sécurité"
|
"power": "Délestage",
|
||||||
|
"security": "Sécurité",
|
||||||
|
"none": "Manuel"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user