Compare commits

...

9 Commits

Author SHA1 Message Date
Jean-Marc Collin ae9b065387 Add logs to diagnose the case 2024-11-05 18:40:14 +00:00
Joeri Colman 2d79d961dc Update en.json (#604)
fixed typo
2024-11-05 10:40:49 +01:00
Jean-Marc Collin 027bf8386b Add message into issue template. 2024-11-05 06:37:27 +00:00
Jean-Marc Collin a0e548ef71 Release 2024-11-03 22:16:15 +00:00
Jean-Marc Collin 132519b471 Merge #590 and fix some tests 2024-11-03 22:12:56 +00:00
hilburn e6c330fc9d Underlying config changes (#590)
* Changes config_flow to allow dynamic length list of underlying entities
Updates previously defined 4x entries to new config style
Changes to thermostat_X to load underlying entities from list
Changes to thermostat X to display underlying entities as a list - COULD BREAK EXISTING TEMPLATES

* Modifies tests to use the new list format

* Added English translation for UI

* Removed all references to individual entities in strings/en.json

* Fix merge mistake

---------

Co-authored-by: Jean-Marc Collin <jm.collin.78@gmail.com>
2024-11-03 22:52:19 +01:00
Jean-Marc Collin 968e8286ea Add some infos 2024-11-03 21:50:42 +00:00
hilburn 0f60c070ab Preset display tweaks (#599)
* Addded Frost Preset to translations
Added Icons for Shedding, Safety, Manual and Frost Presets

* Fixed French Translation
2024-11-03 11:50:37 +01:00
Jean-Marc Collin 810430f7b1 Update README.md
#597
2024-11-02 19:16:24 +01:00
21 changed files with 283 additions and 299 deletions
+2
View File
@@ -4,6 +4,8 @@ about: Create a report to help us improve
--- ---
> Please read carefuly this instructions and fill this form before writing an issue. It helps me to help you.
<!-- This template will allow the maintainer to be efficient and post the more accurante response as possible. There is many types / modes / configuration possible, so the analysis can be very tricky. If don't follow this template, your issue could be rejected without any message. Please help me to help you. --> <!-- This template will allow the maintainer to be efficient and post the more accurante response as possible. There is many types / modes / configuration possible, so the analysis can be very tricky. If don't follow this template, your issue could be rejected without any message. Please help me to help you. -->
<!-- Before you open a new issue, search through the existing issues to see if others have had the same problem. <!-- Before you open a new issue, search through the existing issues to see if others have had the same problem.
+3 -3
View File
@@ -1172,9 +1172,9 @@ Custom attributes are the following:
| ``is_controlled_by_central_mode`` | True if the VTherm can be centrally controlled | | ``is_controlled_by_central_mode`` | True if the VTherm can be centrally controlled |
| ``last_central_mode`` | The last central mode used (None if the VTherm is not centrally controlled) | | ``last_central_mode`` | The last central mode used (None if the VTherm is not centrally controlled) |
| ``is_used_by_central_boiler`` | Indicate if the VTherm can control the central boiler | | ``is_used_by_central_boiler`` | Indicate if the VTherm can control the central boiler |
| ``auto_start_stop_enable`` | Indique si le VTherm est autorisé à s'auto démarrer/arrêter | | ``auto_start_stop_enable`` | Indicate if the VTherm is allowed to do auto start and stop |
| ``auto_start_stop_level`` | Indique le niveau d'auto start/stop | | ``auto_start_stop_level`` | Give the level of auto start/stop |
| ``hvac_off_reason`` | Indique la raison de l'arrêt (hvac_off) du VTherm. Ce peut être Window, Auto-start/stop ou Manuel | | ``hvac_off_reason`` | Give the reason of stop of the VTherm. This could be Window, Auto-start/stop or Manual |
# Some results # Some results
@@ -38,6 +38,22 @@ from .const import (
CONF_USE_CENTRAL_BOILER_FEATURE, CONF_USE_CENTRAL_BOILER_FEATURE,
CONF_POWER_SENSOR, CONF_POWER_SENSOR,
CONF_PRESENCE_SENSOR, CONF_PRESENCE_SENSOR,
CONF_UNDERLYING_LIST,
CONF_HEATER,
CONF_HEATER_2,
CONF_HEATER_3,
CONF_HEATER_4,
CONF_CLIMATE,
CONF_CLIMATE_2,
CONF_CLIMATE_3,
CONF_CLIMATE_4,
CONF_VALVE,
CONF_VALVE_2,
CONF_VALVE_3,
CONF_VALVE_4,
CONF_THERMOSTAT_SWITCH,
CONF_THERMOSTAT_CLIMATE,
CONF_THERMOSTAT_VALVE,
) )
from .vtherm_api import VersatileThermostatAPI from .vtherm_api import VersatileThermostatAPI
@@ -208,10 +224,9 @@ async def async_migrate_entry(hass: HomeAssistant, config_entry: ConfigEntry):
) )
new = {**config_entry.data} new = {**config_entry.data}
if ( thermostat_type = config_entry.data.get(CONF_THERMOSTAT_TYPE)
config_entry.data.get(CONF_THERMOSTAT_TYPE)
== CONF_THERMOSTAT_CENTRAL_CONFIG if thermostat_type == CONF_THERMOSTAT_CENTRAL_CONFIG:
):
new[CONF_USE_WINDOW_FEATURE] = True new[CONF_USE_WINDOW_FEATURE] = True
new[CONF_USE_MOTION_FEATURE] = True new[CONF_USE_MOTION_FEATURE] = True
new[CONF_USE_POWER_FEATURE] = new.get(CONF_POWER_SENSOR, None) is not None new[CONF_USE_POWER_FEATURE] = new.get(CONF_POWER_SENSOR, None) is not None
@@ -223,6 +238,50 @@ async def async_migrate_entry(hass: HomeAssistant, config_entry: ConfigEntry):
"add_central_boiler_control", False "add_central_boiler_control", False
) or new.get(CONF_USE_CENTRAL_BOILER_FEATURE, False) ) or new.get(CONF_USE_CENTRAL_BOILER_FEATURE, False)
if config_entry.data.get(CONF_UNDERLYING_LIST, None) is None:
underlying_list = []
if thermostat_type == CONF_THERMOSTAT_SWITCH:
underlying_list = [
config_entry.data.get(CONF_HEATER, None),
config_entry.data.get(CONF_HEATER_2, None),
config_entry.data.get(CONF_HEATER_3, None),
config_entry.data.get(CONF_HEATER_4, None),
]
elif thermostat_type == CONF_THERMOSTAT_CLIMATE:
underlying_list = [
config_entry.data.get(CONF_CLIMATE, None),
config_entry.data.get(CONF_CLIMATE_2, None),
config_entry.data.get(CONF_CLIMATE_3, None),
config_entry.data.get(CONF_CLIMATE_4, None),
]
elif thermostat_type == CONF_THERMOSTAT_VALVE:
underlying_list = [
config_entry.data.get(CONF_VALVE, None),
config_entry.data.get(CONF_VALVE_2, None),
config_entry.data.get(CONF_VALVE_3, None),
config_entry.data.get(CONF_VALVE_4, None),
]
new[CONF_UNDERLYING_LIST] = [
entity for entity in underlying_list if entity is not None
]
for key in [
CONF_HEATER,
CONF_HEATER_2,
CONF_HEATER_3,
CONF_HEATER_4,
CONF_CLIMATE,
CONF_CLIMATE_2,
CONF_CLIMATE_3,
CONF_CLIMATE_4,
CONF_VALVE,
CONF_VALVE_2,
CONF_VALVE_3,
CONF_VALVE_4,
]:
new.pop(key, None)
hass.config_entries.async_update_entry( hass.config_entries.async_update_entry(
config_entry, config_entry,
data=new, data=new,
@@ -84,6 +84,10 @@ def get_tz(hass: HomeAssistant):
return dt_util.get_time_zone(hass.config.time_zone) return dt_util.get_time_zone(hass.config.time_zone)
_LOGGER_ENERGY = logging.getLogger(
"custom_components.versatile_thermostat.energy_debug"
)
class BaseThermostat(ClimateEntity, RestoreEntity, Generic[T]): class BaseThermostat(ClimateEntity, RestoreEntity, Generic[T]):
"""Representation of a base class for all Versatile Thermostat device.""" """Representation of a base class for all Versatile Thermostat device."""
@@ -198,6 +202,7 @@ class BaseThermostat(ClimateEntity, RestoreEntity, Generic[T]):
self._attr_translation_key = "versatile_thermostat" self._attr_translation_key = "versatile_thermostat"
self._total_energy = None self._total_energy = None
_LOGGER_ENERGY.debug("%s - _init_ resetting energy to None", self)
# because energy of climate is calculated in the thermostat we have to keep that here and not in underlying entity # because energy of climate is calculated in the thermostat we have to keep that here and not in underlying entity
self._underlying_climate_start_hvac_action_date = None self._underlying_climate_start_hvac_action_date = None
@@ -470,6 +475,7 @@ class BaseThermostat(ClimateEntity, RestoreEntity, Generic[T]):
self._presence_state = None self._presence_state = None
self._total_energy = None self._total_energy = None
_LOGGER_ENERGY.debug("%s - post_init_ resetting energy to None", self)
# Read the parameter from configuration.yaml if it exists # Read the parameter from configuration.yaml if it exists
short_ema_params = DEFAULT_SHORT_EMA_PARAMS short_ema_params = DEFAULT_SHORT_EMA_PARAMS
@@ -804,6 +810,11 @@ class BaseThermostat(ClimateEntity, RestoreEntity, Generic[T]):
old_total_energy = old_state.attributes.get(ATTR_TOTAL_ENERGY) old_total_energy = old_state.attributes.get(ATTR_TOTAL_ENERGY)
self._total_energy = old_total_energy if old_total_energy is not None else 0 self._total_energy = old_total_energy if old_total_energy is not None else 0
_LOGGER_ENERGY.debug(
"%s - get_my_previous_state restored energy is %s",
self,
self._total_energy,
)
self.restore_specific_previous_state(old_state) self.restore_specific_previous_state(old_state)
else: else:
@@ -817,6 +828,11 @@ class BaseThermostat(ClimateEntity, RestoreEntity, Generic[T]):
"No previously saved temperature, setting to %s", self._target_temp "No previously saved temperature, setting to %s", self._target_temp
) )
self._total_energy = 0 self._total_energy = 0
_LOGGER_ENERGY.debug(
"%s - get_my_previous_state no previous state energy is %s",
self,
self._total_energy,
)
if not self._hvac_mode: if not self._hvac_mode:
self._hvac_mode = HVACMode.OFF self._hvac_mode = HVACMode.OFF
@@ -2622,6 +2638,22 @@ class BaseThermostat(ClimateEntity, RestoreEntity, Generic[T]):
"hvac_off_reason": self.hvac_off_reason, "hvac_off_reason": self.hvac_off_reason,
} }
_LOGGER_ENERGY.debug(
"%s - update_custom_attributes saved energy is %s",
self,
self.total_energy,
)
@overrides
def async_write_ha_state(self):
"""overrides to have log"""
_LOGGER_ENERGY.debug(
"%s - async_write_ha_state written state energy is %s",
self,
self._total_energy,
)
return super().async_write_ha_state()
@callback @callback
def async_registry_entry_updated(self): def async_registry_entry_updated(self):
"""update the entity if the config entry have been updated """update the entity if the config entry have been updated
@@ -17,7 +17,6 @@ from .const import DOMAIN, DEVICE_MANUFACTURER, ServiceConfigurationError
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
def get_tz(hass: HomeAssistant): def get_tz(hass: HomeAssistant):
"""Get the current timezone""" """Get the current timezone"""
@@ -165,7 +165,7 @@ class VersatileThermostatBaseConfigFlow(FlowHandler):
# check the heater_entity_id # check the heater_entity_id
for conf in [ for conf in [
CONF_HEATER, CONF_UNDERLYING_LIST,
CONF_TEMP_SENSOR, CONF_TEMP_SENSOR,
CONF_EXTERNAL_TEMP_SENSOR, CONF_EXTERNAL_TEMP_SENSOR,
CONF_WINDOW_SENSOR, CONF_WINDOW_SENSOR,
@@ -173,15 +173,17 @@ class VersatileThermostatBaseConfigFlow(FlowHandler):
CONF_POWER_SENSOR, CONF_POWER_SENSOR,
CONF_MAX_POWER_SENSOR, CONF_MAX_POWER_SENSOR,
CONF_PRESENCE_SENSOR, CONF_PRESENCE_SENSOR,
CONF_CLIMATE,
]: ]:
d = data.get(conf, None) # pylint: disable=invalid-name d = data.get(conf, None) # pylint: disable=invalid-name
if d is not None and self.hass.states.get(d) is None: if not isinstance(d, list):
_LOGGER.error( d = [d]
"Entity id %s doesn't have any state. We cannot use it in the Versatile Thermostat configuration", # pylint: disable=line-too-long for e in d:
d, if e is not None and self.hass.states.get(e) is None:
) _LOGGER.error(
raise UnknownEntity(conf) "Entity id %s doesn't have any state. We cannot use it in the Versatile Thermostat configuration", # pylint: disable=line-too-long
e,
)
raise UnknownEntity(conf)
# Check that only one window feature is used # Check that only one window feature is used
ws = self._infos.get(CONF_WINDOW_SENSOR) # pylint: disable=invalid-name ws = self._infos.get(CONF_WINDOW_SENSOR) # pylint: disable=invalid-name
@@ -270,21 +272,8 @@ class VersatileThermostatBaseConfigFlow(FlowHandler):
): ):
return False return False
if ( if infos.get(CONF_UNDERLYING_LIST, None) is not None and not infos.get(
infos.get(CONF_THERMOSTAT_TYPE) == CONF_THERMOSTAT_SWITCH CONF_UNDERLYING_LIST, None
and infos.get(CONF_HEATER, None) is None
):
return False
if (
infos.get(CONF_THERMOSTAT_TYPE) == CONF_THERMOSTAT_CLIMATE
and infos.get(CONF_CLIMATE, None) is None
):
return False
if (
infos.get(CONF_THERMOSTAT_TYPE) == CONF_THERMOSTAT_VALVE
and infos.get(CONF_VALVE, None) is None
): ):
return False return False
@@ -119,17 +119,10 @@ STEP_CENTRAL_BOILER_SCHEMA = vol.Schema(
STEP_THERMOSTAT_SWITCH = vol.Schema( # pylint: disable=invalid-name STEP_THERMOSTAT_SWITCH = vol.Schema( # pylint: disable=invalid-name
{ {
vol.Required(CONF_HEATER): selector.EntitySelector( vol.Required(CONF_UNDERLYING_LIST): selector.EntitySelector(
selector.EntitySelectorConfig(domain=[SWITCH_DOMAIN, INPUT_BOOLEAN_DOMAIN]), selector.EntitySelectorConfig(
), domain=[SWITCH_DOMAIN, INPUT_BOOLEAN_DOMAIN], multiple=True
vol.Optional(CONF_HEATER_2): selector.EntitySelector( ),
selector.EntitySelectorConfig(domain=[SWITCH_DOMAIN, INPUT_BOOLEAN_DOMAIN]),
),
vol.Optional(CONF_HEATER_3): selector.EntitySelector(
selector.EntitySelectorConfig(domain=[SWITCH_DOMAIN, INPUT_BOOLEAN_DOMAIN]),
),
vol.Optional(CONF_HEATER_4): selector.EntitySelector(
selector.EntitySelectorConfig(domain=[SWITCH_DOMAIN, INPUT_BOOLEAN_DOMAIN]),
), ),
vol.Optional(CONF_HEATER_KEEP_ALIVE): cv.positive_int, vol.Optional(CONF_HEATER_KEEP_ALIVE): cv.positive_int,
vol.Required(CONF_PROP_FUNCTION, default=PROPORTIONAL_FUNCTION_TPI): vol.In( vol.Required(CONF_PROP_FUNCTION, default=PROPORTIONAL_FUNCTION_TPI): vol.In(
@@ -144,17 +137,8 @@ STEP_THERMOSTAT_SWITCH = vol.Schema( # pylint: disable=invalid-name
STEP_THERMOSTAT_CLIMATE = vol.Schema( # pylint: disable=invalid-name STEP_THERMOSTAT_CLIMATE = vol.Schema( # pylint: disable=invalid-name
{ {
vol.Required(CONF_CLIMATE): selector.EntitySelector( vol.Required(CONF_UNDERLYING_LIST): selector.EntitySelector(
selector.EntitySelectorConfig(domain=CLIMATE_DOMAIN), selector.EntitySelectorConfig(domain=CLIMATE_DOMAIN, multiple=True),
),
vol.Optional(CONF_CLIMATE_2): selector.EntitySelector(
selector.EntitySelectorConfig(domain=CLIMATE_DOMAIN),
),
vol.Optional(CONF_CLIMATE_3): selector.EntitySelector(
selector.EntitySelectorConfig(domain=CLIMATE_DOMAIN),
),
vol.Optional(CONF_CLIMATE_4): selector.EntitySelector(
selector.EntitySelectorConfig(domain=CLIMATE_DOMAIN),
), ),
vol.Optional(CONF_AC_MODE, default=False): cv.boolean, vol.Optional(CONF_AC_MODE, default=False): cv.boolean,
vol.Optional( vol.Optional(
@@ -183,17 +167,10 @@ STEP_THERMOSTAT_CLIMATE = vol.Schema( # pylint: disable=invalid-name
STEP_THERMOSTAT_VALVE = vol.Schema( # pylint: disable=invalid-name STEP_THERMOSTAT_VALVE = vol.Schema( # pylint: disable=invalid-name
{ {
vol.Required(CONF_VALVE): selector.EntitySelector( vol.Required(CONF_UNDERLYING_LIST): selector.EntitySelector(
selector.EntitySelectorConfig(domain=[NUMBER_DOMAIN, INPUT_NUMBER_DOMAIN]), selector.EntitySelectorConfig(
), domain=[NUMBER_DOMAIN, INPUT_NUMBER_DOMAIN], multiple=True
vol.Optional(CONF_VALVE_2): selector.EntitySelector( ),
selector.EntitySelectorConfig(domain=[NUMBER_DOMAIN, INPUT_NUMBER_DOMAIN]),
),
vol.Optional(CONF_VALVE_3): selector.EntitySelector(
selector.EntitySelectorConfig(domain=[NUMBER_DOMAIN, INPUT_NUMBER_DOMAIN]),
),
vol.Optional(CONF_VALVE_4): selector.EntitySelector(
selector.EntitySelectorConfig(domain=[NUMBER_DOMAIN, INPUT_NUMBER_DOMAIN]),
), ),
vol.Required(CONF_PROP_FUNCTION, default=PROPORTIONAL_FUNCTION_TPI): vol.In( vol.Required(CONF_PROP_FUNCTION, default=PROPORTIONAL_FUNCTION_TPI): vol.In(
[ [
+22 -27
View File
@@ -23,8 +23,8 @@ from .prop_algorithm import (
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
CONFIG_VERSION = 1 CONFIG_VERSION = 2
CONFIG_MINOR_VERSION = 2 CONFIG_MINOR_VERSION = 0
PRESET_TEMP_SUFFIX = "_temp" PRESET_TEMP_SUFFIX = "_temp"
PRESET_AC_SUFFIX = "_ac" PRESET_AC_SUFFIX = "_ac"
@@ -55,10 +55,7 @@ PLATFORMS: list[Platform] = [
Platform.SWITCH, Platform.SWITCH,
] ]
CONF_HEATER = "heater_entity_id" CONF_UNDERLYING_LIST = "underlying_entity_ids"
CONF_HEATER_2 = "heater_entity2_id"
CONF_HEATER_3 = "heater_entity3_id"
CONF_HEATER_4 = "heater_entity4_id"
CONF_HEATER_KEEP_ALIVE = "heater_keep_alive" CONF_HEATER_KEEP_ALIVE = "heater_keep_alive"
CONF_TEMP_SENSOR = "temperature_sensor_entity_id" CONF_TEMP_SENSOR = "temperature_sensor_entity_id"
CONF_LAST_SEEN_TEMP_SENSOR = "last_seen_temperature_sensor_entity_id" CONF_LAST_SEEN_TEMP_SENSOR = "last_seen_temperature_sensor_entity_id"
@@ -90,10 +87,6 @@ CONF_THERMOSTAT_CENTRAL_CONFIG = "thermostat_central_config"
CONF_THERMOSTAT_SWITCH = "thermostat_over_switch" CONF_THERMOSTAT_SWITCH = "thermostat_over_switch"
CONF_THERMOSTAT_CLIMATE = "thermostat_over_climate" CONF_THERMOSTAT_CLIMATE = "thermostat_over_climate"
CONF_THERMOSTAT_VALVE = "thermostat_over_valve" CONF_THERMOSTAT_VALVE = "thermostat_over_valve"
CONF_CLIMATE = "climate_entity_id"
CONF_CLIMATE_2 = "climate_entity2_id"
CONF_CLIMATE_3 = "climate_entity3_id"
CONF_CLIMATE_4 = "climate_entity4_id"
CONF_USE_WINDOW_FEATURE = "use_window_feature" CONF_USE_WINDOW_FEATURE = "use_window_feature"
CONF_USE_MOTION_FEATURE = "use_motion_feature" CONF_USE_MOTION_FEATURE = "use_motion_feature"
CONF_USE_PRESENCE_FEATURE = "use_presence_feature" CONF_USE_PRESENCE_FEATURE = "use_presence_feature"
@@ -104,10 +97,6 @@ CONF_AC_MODE = "ac_mode"
CONF_WINDOW_AUTO_OPEN_THRESHOLD = "window_auto_open_threshold" CONF_WINDOW_AUTO_OPEN_THRESHOLD = "window_auto_open_threshold"
CONF_WINDOW_AUTO_CLOSE_THRESHOLD = "window_auto_close_threshold" CONF_WINDOW_AUTO_CLOSE_THRESHOLD = "window_auto_close_threshold"
CONF_WINDOW_AUTO_MAX_DURATION = "window_auto_max_duration" CONF_WINDOW_AUTO_MAX_DURATION = "window_auto_max_duration"
CONF_VALVE = "valve_entity_id"
CONF_VALVE_2 = "valve_entity2_id"
CONF_VALVE_3 = "valve_entity3_id"
CONF_VALVE_4 = "valve_entity4_id"
CONF_AUTO_REGULATION_MODE = "auto_regulation_mode" CONF_AUTO_REGULATION_MODE = "auto_regulation_mode"
CONF_AUTO_REGULATION_NONE = "auto_regulation_none" CONF_AUTO_REGULATION_NONE = "auto_regulation_none"
CONF_AUTO_REGULATION_SLOW = "auto_regulation_slow" CONF_AUTO_REGULATION_SLOW = "auto_regulation_slow"
@@ -127,6 +116,20 @@ CONF_AUTO_FAN_HIGH = "auto_fan_high"
CONF_AUTO_FAN_TURBO = "auto_fan_turbo" CONF_AUTO_FAN_TURBO = "auto_fan_turbo"
CONF_STEP_TEMPERATURE = "step_temperature" CONF_STEP_TEMPERATURE = "step_temperature"
# Deprecated
CONF_HEATER = "heater_entity_id"
CONF_HEATER_2 = "heater_entity2_id"
CONF_HEATER_3 = "heater_entity3_id"
CONF_HEATER_4 = "heater_entity4_id"
CONF_CLIMATE = "climate_entity_id"
CONF_CLIMATE_2 = "climate_entity2_id"
CONF_CLIMATE_3 = "climate_entity3_id"
CONF_CLIMATE_4 = "climate_entity4_id"
CONF_VALVE = "valve_entity_id"
CONF_VALVE_2 = "valve_entity2_id"
CONF_VALVE_3 = "valve_entity3_id"
CONF_VALVE_4 = "valve_entity4_id"
# Global params into configuration.yaml # Global params into configuration.yaml
CONF_SHORT_EMA_PARAMS = "short_ema_params" CONF_SHORT_EMA_PARAMS = "short_ema_params"
CONF_SAFETY_MODE = "safety_mode" CONF_SAFETY_MODE = "safety_mode"
@@ -249,10 +252,6 @@ CONF_PRESETS_AWAY_WITH_AC_VALUES = list(CONF_PRESETS_AWAY_WITH_AC.values())
ALL_CONF = ( ALL_CONF = (
[ [
CONF_NAME, CONF_NAME,
CONF_HEATER,
CONF_HEATER_2,
CONF_HEATER_3,
CONF_HEATER_4,
CONF_HEATER_KEEP_ALIVE, CONF_HEATER_KEEP_ALIVE,
CONF_TEMP_SENSOR, CONF_TEMP_SENSOR,
CONF_EXTERNAL_TEMP_SENSOR, CONF_EXTERNAL_TEMP_SENSOR,
@@ -282,20 +281,12 @@ ALL_CONF = (
CONF_THERMOSTAT_TYPE, CONF_THERMOSTAT_TYPE,
CONF_THERMOSTAT_SWITCH, CONF_THERMOSTAT_SWITCH,
CONF_THERMOSTAT_CLIMATE, CONF_THERMOSTAT_CLIMATE,
CONF_CLIMATE,
CONF_CLIMATE_2,
CONF_CLIMATE_3,
CONF_CLIMATE_4,
CONF_USE_WINDOW_FEATURE, CONF_USE_WINDOW_FEATURE,
CONF_USE_MOTION_FEATURE, CONF_USE_MOTION_FEATURE,
CONF_USE_PRESENCE_FEATURE, CONF_USE_PRESENCE_FEATURE,
CONF_USE_POWER_FEATURE, CONF_USE_POWER_FEATURE,
CONF_USE_CENTRAL_BOILER_FEATURE, CONF_USE_CENTRAL_BOILER_FEATURE,
CONF_AC_MODE, CONF_AC_MODE,
CONF_VALVE,
CONF_VALVE_2,
CONF_VALVE_3,
CONF_VALVE_4,
CONF_AUTO_REGULATION_MODE, CONF_AUTO_REGULATION_MODE,
CONF_AUTO_REGULATION_DTEMP, CONF_AUTO_REGULATION_DTEMP,
CONF_AUTO_REGULATION_PERIOD_MIN, CONF_AUTO_REGULATION_PERIOD_MIN,
@@ -363,7 +354,11 @@ CONF_WINDOW_ACTIONS = [
CONF_WINDOW_ECO_TEMP, CONF_WINDOW_ECO_TEMP,
] ]
SUPPORT_FLAGS = ClimateEntityFeature.TARGET_TEMPERATURE | ClimateEntityFeature.TURN_OFF | ClimateEntityFeature.TURN_ON SUPPORT_FLAGS = (
ClimateEntityFeature.TARGET_TEMPERATURE
| ClimateEntityFeature.TURN_OFF
| ClimateEntityFeature.TURN_ON
)
SERVICE_SET_PRESENCE = "set_presence" SERVICE_SET_PRESENCE = "set_presence"
SERVICE_SET_PRESET_TEMPERATURE = "set_preset_temperature" SERVICE_SET_PRESET_TEMPERATURE = "set_preset_temperature"
@@ -0,0 +1,18 @@
{
"entity": {
"climate": {
"versatile_thermostat": {
"state_attributes": {
"preset_mode": {
"state": {
"shedding": "mdi:power-plug-off",
"safety": "mdi:shield-alert",
"none": "mdi:knob",
"frost": "mdi:snowflake"
}
}
}
}
}
}
}
@@ -14,6 +14,6 @@
"quality_scale": "silver", "quality_scale": "silver",
"requirements": [], "requirements": [],
"ssdp": [], "ssdp": [],
"version": "6.5.0", "version": "6.6.0",
"zeroconf": [] "zeroconf": []
} }
@@ -72,21 +72,10 @@
"title": "Linked entities", "title": "Linked entities",
"description": "Linked entities attributes", "description": "Linked entities attributes",
"data": { "data": {
"heater_entity_id": "1st heater switch", "underlying_entity_ids": "The device(s) to be controlled",
"heater_entity2_id": "2nd heater switch",
"heater_entity3_id": "3rd heater switch",
"heater_entity4_id": "4th heater switch",
"heater_keep_alive": "Switch keep-alive interval in seconds", "heater_keep_alive": "Switch keep-alive interval in seconds",
"proportional_function": "Algorithm", "proportional_function": "Algorithm",
"climate_entity_id": "1st underlying climate",
"climate_entity2_id": "2nd underlying climate",
"climate_entity3_id": "3rd underlying climate",
"climate_entity4_id": "4th underlying climate",
"ac_mode": "AC mode", "ac_mode": "AC mode",
"valve_entity_id": "1st valve number",
"valve_entity2_id": "2nd valve number",
"valve_entity3_id": "3rd valve number",
"valve_entity4_id": "4th valve number",
"auto_regulation_mode": "Self-regulation", "auto_regulation_mode": "Self-regulation",
"auto_regulation_dtemp": "Regulation threshold", "auto_regulation_dtemp": "Regulation threshold",
"auto_regulation_periode_min": "Regulation minimum period", "auto_regulation_periode_min": "Regulation minimum period",
@@ -95,21 +84,10 @@
"auto_fan_mode": "Auto fan mode" "auto_fan_mode": "Auto fan mode"
}, },
"data_description": { "data_description": {
"heater_entity_id": "Mandatory heater entity id", "underlying_entity_ids": "The device(s) to be controlled - 1 is required",
"heater_entity2_id": "Optional 2nd Heater entity id. Leave empty if not required",
"heater_entity3_id": "Optional 3rd Heater entity id. Leave empty if not required",
"heater_entity4_id": "Optional 4th Heater entity id. Leave empty if not required",
"heater_keep_alive": "Optional heater switch state refresh interval. Leave empty if not required.", "heater_keep_alive": "Optional heater switch state refresh interval. Leave empty if not required.",
"proportional_function": "Algorithm to use (TPI is the only one for now)", "proportional_function": "Algorithm to use (TPI is the only one for now)",
"climate_entity_id": "Underlying climate entity id",
"climate_entity2_id": "2nd underlying climate entity id",
"climate_entity3_id": "3rd underlying climate entity id",
"climate_entity4_id": "4th underlying climate entity id",
"ac_mode": "Use the Air Conditioning (AC) mode", "ac_mode": "Use the Air Conditioning (AC) mode",
"valve_entity_id": "1st valve number entity id",
"valve_entity2_id": "2nd valve number entity id",
"valve_entity3_id": "3rd valve number entity id",
"valve_entity4_id": "4th valve number entity id",
"auto_regulation_mode": "Auto adjustment of the target temperature", "auto_regulation_mode": "Auto adjustment of the target temperature",
"auto_regulation_dtemp": "The threshold in ° (or % for valve) under which the temperature change will not be sent", "auto_regulation_dtemp": "The threshold in ° (or % for valve) under which the temperature change will not be sent",
"auto_regulation_periode_min": "Duration in minutes between two regulation update", "auto_regulation_periode_min": "Duration in minutes between two regulation update",
@@ -309,21 +287,10 @@
"title": "Entities - {name}", "title": "Entities - {name}",
"description": "Linked entities attributes", "description": "Linked entities attributes",
"data": { "data": {
"heater_entity_id": "1st heater switch", "underlying_entity_ids": "The device(s) to be controlled",
"heater_entity2_id": "2nd heater switch",
"heater_entity3_id": "3rd heater switch",
"heater_entity4_id": "4th heater switch",
"heater_keep_alive": "Switch keep-alive interval in seconds", "heater_keep_alive": "Switch keep-alive interval in seconds",
"proportional_function": "Algorithm", "proportional_function": "Algorithm",
"climate_entity_id": "1st underlying climate",
"climate_entity2_id": "2nd underlying climate",
"climate_entity3_id": "3rd underlying climate",
"climate_entity4_id": "4th underlying climate",
"ac_mode": "AC mode", "ac_mode": "AC mode",
"valve_entity_id": "1st valve number",
"valve_entity2_id": "2nd valve number",
"valve_entity3_id": "3rd valve number",
"valve_entity4_id": "4th valve number",
"auto_regulation_mode": "Self-regulation", "auto_regulation_mode": "Self-regulation",
"auto_regulation_dtemp": "Regulation threshold", "auto_regulation_dtemp": "Regulation threshold",
"auto_regulation_periode_min": "Regulation minimum period", "auto_regulation_periode_min": "Regulation minimum period",
@@ -332,21 +299,10 @@
"auto_fan_mode": "Auto fan mode" "auto_fan_mode": "Auto fan mode"
}, },
"data_description": { "data_description": {
"heater_entity_id": "Mandatory heater entity id", "underlying_entity_ids": "The device(s) to be controlled - 1 is required",
"heater_entity2_id": "Optional 2nd Heater entity id. Leave empty if not used",
"heater_entity3_id": "Optional 3rd Heater entity id. Leave empty if not used",
"heater_entity4_id": "Optional 4th Heater entity id. Leave empty if not used",
"heater_keep_alive": "Optional heater switch state refresh interval. Leave empty if not required.", "heater_keep_alive": "Optional heater switch state refresh interval. Leave empty if not required.",
"proportional_function": "Algorithm to use (TPI is the only one for now)", "proportional_function": "Algorithm to use (TPI is the only one for now)",
"climate_entity_id": "Underlying climate entity id",
"climate_entity2_id": "2nd underlying climate entity id",
"climate_entity3_id": "3rd underlying climate entity id",
"climate_entity4_id": "4th underlying climate entity id",
"ac_mode": "Use the Air Conditioning (AC) mode", "ac_mode": "Use the Air Conditioning (AC) mode",
"valve_entity_id": "1st valve number entity id",
"valve_entity2_id": "2nd valve number entity id",
"valve_entity3_id": "3rd valve number entity id",
"valve_entity4_id": "4th valve number entity id",
"auto_regulation_mode": "Auto adjustment of the target temperature", "auto_regulation_mode": "Auto adjustment of the target temperature",
"auto_regulation_dtemp": "The threshold in ° (or % for valve) under which the temperature change will not be sent", "auto_regulation_dtemp": "The threshold in ° (or % for valve) under which the temperature change will not be sent",
"auto_regulation_periode_min": "Duration in minutes between two regulation update", "auto_regulation_periode_min": "Duration in minutes between two regulation update",
@@ -536,7 +492,8 @@
"state": { "state": {
"power": "Shedding", "power": "Shedding",
"security": "Safety", "security": "Safety",
"none": "Manual" "none": "Manual",
"frost": "Frost"
} }
} }
} }
@@ -58,7 +58,7 @@ class AutoStartStopEnable(VersatileThermostatBaseEntity, SwitchEntity, RestoreEn
@property @property
def icon(self) -> str | None: def icon(self) -> str | None:
"""The icon""" """The icon"""
return "mdi:power-settings" return "mdi:power-sleep"
async def async_added_to_hass(self): async def async_added_to_hass(self):
await super().async_added_to_hass() await super().async_added_to_hass()
@@ -31,6 +31,10 @@ from .auto_start_stop_algorithm import (
) )
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
_LOGGER_ENERGY = logging.getLogger(
"custom_components.versatile_thermostat.energy_debug"
)
HVAC_ACTION_ON = [ # pylint: disable=invalid-name HVAC_ACTION_ON = [ # pylint: disable=invalid-name
HVACAction.COOLING, HVACAction.COOLING,
@@ -65,10 +69,7 @@ class ThermostatOverClimate(BaseThermostat[UnderlyingClimate]):
{ {
"is_over_climate", "is_over_climate",
"start_hvac_action_date", "start_hvac_action_date",
"underlying_climate_0", "underlying_entities",
"underlying_climate_1",
"underlying_climate_2",
"underlying_climate_3",
"regulation_accumulated_error", "regulation_accumulated_error",
"auto_regulation_mode", "auto_regulation_mode",
"auto_fan_mode", "auto_fan_mode",
@@ -100,20 +101,15 @@ class ThermostatOverClimate(BaseThermostat[UnderlyingClimate]):
"""Initialize the Thermostat""" """Initialize the Thermostat"""
super().post_init(config_entry) super().post_init(config_entry)
for climate in [
CONF_CLIMATE, for climate in config_entry.get(CONF_UNDERLYING_LIST):
CONF_CLIMATE_2, self._underlyings.append(
CONF_CLIMATE_3, UnderlyingClimate(
CONF_CLIMATE_4, hass=self._hass,
]: thermostat=self,
if config_entry.get(climate): climate_entity_id=climate,
self._underlyings.append(
UnderlyingClimate(
hass=self._hass,
thermostat=self,
climate_entity_id=config_entry.get(climate),
)
) )
)
self.choose_auto_regulation_mode( self.choose_auto_regulation_mode(
config_entry.get(CONF_AUTO_REGULATION_MODE) config_entry.get(CONF_AUTO_REGULATION_MODE)
@@ -504,18 +500,10 @@ class ThermostatOverClimate(BaseThermostat[UnderlyingClimate]):
self._attr_extra_state_attributes["start_hvac_action_date"] = ( self._attr_extra_state_attributes["start_hvac_action_date"] = (
self._underlying_climate_start_hvac_action_date self._underlying_climate_start_hvac_action_date
) )
self._attr_extra_state_attributes["underlying_climate_0"] = self._underlyings[
0 self._attr_extra_state_attributes["underlying_entities"] = [
].entity_id underlying.entity_id for underlying in self._underlyings
self._attr_extra_state_attributes["underlying_climate_1"] = ( ]
self._underlyings[1].entity_id if len(self._underlyings) > 1 else None
)
self._attr_extra_state_attributes["underlying_climate_2"] = (
self._underlyings[2].entity_id if len(self._underlyings) > 2 else None
)
self._attr_extra_state_attributes["underlying_climate_3"] = (
self._underlyings[3].entity_id if len(self._underlyings) > 3 else None
)
if self.is_regulated: if self.is_regulated:
self._attr_extra_state_attributes["is_regulated"] = self.is_regulated self._attr_extra_state_attributes["is_regulated"] = self.is_regulated
@@ -565,6 +553,7 @@ class ThermostatOverClimate(BaseThermostat[UnderlyingClimate]):
] = self._auto_start_stop_algo.accumulated_error_threshold ] = self._auto_start_stop_algo.accumulated_error_threshold
self.async_write_ha_state() self.async_write_ha_state()
_LOGGER.debug( _LOGGER.debug(
"%s - Calling update_custom_attributes: %s", "%s - Calling update_custom_attributes: %s",
self, self,
@@ -611,8 +600,18 @@ class ThermostatOverClimate(BaseThermostat[UnderlyingClimate]):
if self._total_energy is None: if self._total_energy is None:
self._total_energy = added_energy self._total_energy = added_energy
_LOGGER_ENERGY.debug(
"%s - incremente_energy set energy is %s",
self,
self._total_energy,
)
else: else:
self._total_energy += added_energy self._total_energy += added_energy
_LOGGER_ENERGY.debug(
"%s - incremente_energy incremented energy is %s",
self,
self._total_energy,
)
_LOGGER.debug( _LOGGER.debug(
"%s - added energy is %.3f . Total energy is now: %.3f", "%s - added energy is %.3f . Total energy is now: %.3f",
@@ -910,6 +909,8 @@ class ThermostatOverClimate(BaseThermostat[UnderlyingClimate]):
"target_temperature": self.target_temperature, "target_temperature": self.target_temperature,
"current_temperature": self.current_temperature, "current_temperature": self.current_temperature,
"temperature_slope": round(slope, 3), "temperature_slope": round(slope, 3),
"accumulated_error": self._auto_start_stop_algo.accumulated_error,
"accumulated_error_threshold": self._auto_start_stop_algo.accumulated_error_threshold,
}, },
) )
@@ -933,6 +934,8 @@ class ThermostatOverClimate(BaseThermostat[UnderlyingClimate]):
"target_temperature": self.target_temperature, "target_temperature": self.target_temperature,
"current_temperature": self.current_temperature, "current_temperature": self.current_temperature,
"temperature_slope": round(slope, 3), "temperature_slope": round(slope, 3),
"accumulated_error": self._auto_start_stop_algo.accumulated_error,
"accumulated_error_threshold": self._auto_start_stop_algo.accumulated_error_threshold,
}, },
) )
@@ -10,10 +10,7 @@ from homeassistant.helpers.event import (
from homeassistant.components.climate import HVACMode from homeassistant.components.climate import HVACMode
from .const import ( from .const import (
CONF_HEATER, CONF_UNDERLYING_LIST,
CONF_HEATER_2,
CONF_HEATER_3,
CONF_HEATER_4,
CONF_HEATER_KEEP_ALIVE, CONF_HEATER_KEEP_ALIVE,
CONF_INVERSE_SWITCH, CONF_INVERSE_SWITCH,
overrides, overrides,
@@ -24,7 +21,9 @@ from .underlyings import UnderlyingSwitch
from .prop_algorithm import PropAlgorithm from .prop_algorithm import PropAlgorithm
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
_LOGGER_ENERGY = logging.getLogger(
"custom_components.versatile_thermostat.energy_debug"
)
class ThermostatOverSwitch(BaseThermostat[UnderlyingSwitch]): class ThermostatOverSwitch(BaseThermostat[UnderlyingSwitch]):
"""Representation of a base class for a Versatile Thermostat over a switch.""" """Representation of a base class for a Versatile Thermostat over a switch."""
@@ -35,10 +34,7 @@ class ThermostatOverSwitch(BaseThermostat[UnderlyingSwitch]):
{ {
"is_over_switch", "is_over_switch",
"is_inversed", "is_inversed",
"underlying_switch_0", "underlying_entities",
"underlying_switch_1",
"underlying_switch_2",
"underlying_switch_3",
"on_time_sec", "on_time_sec",
"off_time_sec", "off_time_sec",
"cycle_min", "cycle_min",
@@ -90,13 +86,7 @@ class ThermostatOverSwitch(BaseThermostat[UnderlyingSwitch]):
self.name, self.name,
) )
lst_switches = [config_entry.get(CONF_HEATER)] lst_switches = config_entry.get(CONF_UNDERLYING_LIST)
if config_entry.get(CONF_HEATER_2):
lst_switches.append(config_entry.get(CONF_HEATER_2))
if config_entry.get(CONF_HEATER_3):
lst_switches.append(config_entry.get(CONF_HEATER_3))
if config_entry.get(CONF_HEATER_4):
lst_switches.append(config_entry.get(CONF_HEATER_4))
delta_cycle = self._cycle_min * 60 / len(lst_switches) delta_cycle = self._cycle_min * 60 / len(lst_switches)
for idx, switch in enumerate(lst_switches): for idx, switch in enumerate(lst_switches):
@@ -140,16 +130,10 @@ class ThermostatOverSwitch(BaseThermostat[UnderlyingSwitch]):
self._attr_extra_state_attributes["is_over_switch"] = self.is_over_switch self._attr_extra_state_attributes["is_over_switch"] = self.is_over_switch
self._attr_extra_state_attributes["is_inversed"] = self.is_inversed self._attr_extra_state_attributes["is_inversed"] = self.is_inversed
self._attr_extra_state_attributes["keep_alive_sec"] = under0.keep_alive_sec self._attr_extra_state_attributes["keep_alive_sec"] = under0.keep_alive_sec
self._attr_extra_state_attributes["underlying_switch_0"] = under0.entity_id
self._attr_extra_state_attributes["underlying_switch_1"] = ( self._attr_extra_state_attributes["underlying_entities"] = [
self._underlyings[1].entity_id if len(self._underlyings) > 1 else None underlying.entity_id for underlying in self._underlyings
) ]
self._attr_extra_state_attributes["underlying_switch_2"] = (
self._underlyings[2].entity_id if len(self._underlyings) > 2 else None
)
self._attr_extra_state_attributes["underlying_switch_3"] = (
self._underlyings[3].entity_id if len(self._underlyings) > 3 else None
)
self._attr_extra_state_attributes[ self._attr_extra_state_attributes[
"on_percent" "on_percent"
@@ -201,8 +185,18 @@ class ThermostatOverSwitch(BaseThermostat[UnderlyingSwitch]):
if self._total_energy is None: if self._total_energy is None:
self._total_energy = added_energy self._total_energy = added_energy
_LOGGER_ENERGY.debug(
"%s - incremente_energy set energy is %s",
self,
self._total_energy,
)
else: else:
self._total_energy += added_energy self._total_energy += added_energy
_LOGGER_ENERGY.debug(
"%s - incremente_energy increment energy is %s",
self,
self._total_energy,
)
self.update_custom_attributes() self.update_custom_attributes()
@@ -15,10 +15,7 @@ from .base_thermostat import BaseThermostat, ConfigData
from .prop_algorithm import PropAlgorithm from .prop_algorithm import PropAlgorithm
from .const import ( from .const import (
CONF_VALVE, CONF_UNDERLYING_LIST,
CONF_VALVE_2,
CONF_VALVE_3,
CONF_VALVE_4,
# This is not really self-regulation but regulation here # This is not really self-regulation but regulation here
CONF_AUTO_REGULATION_DTEMP, CONF_AUTO_REGULATION_DTEMP,
CONF_AUTO_REGULATION_PERIOD_MIN, CONF_AUTO_REGULATION_PERIOD_MIN,
@@ -28,7 +25,9 @@ from .const import (
from .underlyings import UnderlyingValve from .underlyings import UnderlyingValve
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
_LOGGER_ENERGY = logging.getLogger(
"custom_components.versatile_thermostat.energy_debug"
)
class ThermostatOverValve(BaseThermostat[UnderlyingValve]): # pylint: disable=abstract-method class ThermostatOverValve(BaseThermostat[UnderlyingValve]): # pylint: disable=abstract-method
"""Representation of a class for a Versatile Thermostat over a Valve""" """Representation of a class for a Versatile Thermostat over a Valve"""
@@ -37,10 +36,7 @@ class ThermostatOverValve(BaseThermostat[UnderlyingValve]): # pylint: disable=a
frozenset( frozenset(
{ {
"is_over_valve", "is_over_valve",
"underlying_valve_0", "underlying_entities",
"underlying_valve_1",
"underlying_valve_2",
"underlying_valve_3",
"on_time_sec", "on_time_sec",
"off_time_sec", "off_time_sec",
"cycle_min", "cycle_min",
@@ -105,13 +101,7 @@ class ThermostatOverValve(BaseThermostat[UnderlyingValve]): # pylint: disable=a
self.name, self.name,
) )
lst_valves = [config_entry.get(CONF_VALVE)] lst_valves = config_entry.get(CONF_UNDERLYING_LIST)
if config_entry.get(CONF_VALVE_2):
lst_valves.append(config_entry.get(CONF_VALVE_2))
if config_entry.get(CONF_VALVE_3):
lst_valves.append(config_entry.get(CONF_VALVE_3))
if config_entry.get(CONF_VALVE_4):
lst_valves.append(config_entry.get(CONF_VALVE_4))
for _, valve in enumerate(lst_valves): for _, valve in enumerate(lst_valves):
self._underlyings.append( self._underlyings.append(
@@ -163,18 +153,10 @@ class ThermostatOverValve(BaseThermostat[UnderlyingValve]): # pylint: disable=a
"valve_open_percent" "valve_open_percent"
] = self.valve_open_percent ] = self.valve_open_percent
self._attr_extra_state_attributes["is_over_valve"] = self.is_over_valve self._attr_extra_state_attributes["is_over_valve"] = self.is_over_valve
self._attr_extra_state_attributes["underlying_valve_0"] = self._underlyings[
0 self._attr_extra_state_attributes["underlying_entities"] = [
].entity_id underlying.entity_id for underlying in self._underlyings
self._attr_extra_state_attributes["underlying_valve_1"] = ( ]
self._underlyings[1].entity_id if len(self._underlyings) > 1 else None
)
self._attr_extra_state_attributes["underlying_valve_2"] = (
self._underlyings[2].entity_id if len(self._underlyings) > 2 else None
)
self._attr_extra_state_attributes["underlying_valve_3"] = (
self._underlyings[3].entity_id if len(self._underlyings) > 3 else None
)
self._attr_extra_state_attributes[ self._attr_extra_state_attributes[
"on_percent" "on_percent"
@@ -285,8 +267,18 @@ class ThermostatOverValve(BaseThermostat[UnderlyingValve]): # pylint: disable=a
if self._total_energy is None: if self._total_energy is None:
self._total_energy = added_energy self._total_energy = added_energy
_LOGGER_ENERGY.debug(
"%s - incremente_energy set energy is %s",
self,
self._total_energy,
)
else: else:
self._total_energy += added_energy self._total_energy += added_energy
_LOGGER_ENERGY.debug(
"%s - get_my_previous_state increment energy is %s",
self,
self._total_energy,
)
self.update_custom_attributes() self.update_custom_attributes()
@@ -64,7 +64,7 @@
"use_motion_feature": "Use motion detection", "use_motion_feature": "Use motion detection",
"use_power_feature": "Use power management", "use_power_feature": "Use power management",
"use_presence_feature": "Use presence detection", "use_presence_feature": "Use presence detection",
"use_central_boiler_feature": "Use a central boiler. Check to add a control to your central boiler. You will have to configure the VTherm which will have a control of the central boiler after seecting this checkbox to take effect. If one VTherm requires heating, the boiler will be turned on. If no VTherm requires heating, the boiler will be turned off. Commands for turning on/off the central boiler are given in the related configuration page", "use_central_boiler_feature": "Use a central boiler. Check to add a control to your central boiler. You will have to configure the VTherm which will have a control of the central boiler after selecting this checkbox to take effect. If one VTherm requires heating, the boiler will be turned on. If no VTherm requires heating, the boiler will be turned off. Commands for turning on/off the central boiler are given in the related configuration page",
"use_auto_start_stop_feature": "Use the auto start and stop feature" "use_auto_start_stop_feature": "Use the auto start and stop feature"
} }
}, },
@@ -72,21 +72,10 @@
"title": "Linked entities", "title": "Linked entities",
"description": "Linked entities attributes", "description": "Linked entities attributes",
"data": { "data": {
"heater_entity_id": "1st heater switch", "underlying_entity_ids": "The device(s) to be controlled",
"heater_entity2_id": "2nd heater switch",
"heater_entity3_id": "3rd heater switch",
"heater_entity4_id": "4th heater switch",
"heater_keep_alive": "Switch keep-alive interval in seconds", "heater_keep_alive": "Switch keep-alive interval in seconds",
"proportional_function": "Algorithm", "proportional_function": "Algorithm",
"climate_entity_id": "1st underlying climate",
"climate_entity2_id": "2nd underlying climate",
"climate_entity3_id": "3rd underlying climate",
"climate_entity4_id": "4th underlying climate",
"ac_mode": "AC mode", "ac_mode": "AC mode",
"valve_entity_id": "1st valve number",
"valve_entity2_id": "2nd valve number",
"valve_entity3_id": "3rd valve number",
"valve_entity4_id": "4th valve number",
"auto_regulation_mode": "Self-regulation", "auto_regulation_mode": "Self-regulation",
"auto_regulation_dtemp": "Regulation threshold", "auto_regulation_dtemp": "Regulation threshold",
"auto_regulation_periode_min": "Regulation minimum period", "auto_regulation_periode_min": "Regulation minimum period",
@@ -95,21 +84,10 @@
"auto_fan_mode": "Auto fan mode" "auto_fan_mode": "Auto fan mode"
}, },
"data_description": { "data_description": {
"heater_entity_id": "Mandatory heater entity id", "underlying_entity_ids": "The device(s) to be controlled - 1 is required",
"heater_entity2_id": "Optional 2nd Heater entity id. Leave empty if not required",
"heater_entity3_id": "Optional 3rd Heater entity id. Leave empty if not required",
"heater_entity4_id": "Optional 4th Heater entity id. Leave empty if not required",
"heater_keep_alive": "Optional heater switch state refresh interval. Leave empty if not required.", "heater_keep_alive": "Optional heater switch state refresh interval. Leave empty if not required.",
"proportional_function": "Algorithm to use (TPI is the only one for now)", "proportional_function": "Algorithm to use (TPI is the only one for now)",
"climate_entity_id": "Underlying climate entity id",
"climate_entity2_id": "2nd underlying climate entity id",
"climate_entity3_id": "3rd underlying climate entity id",
"climate_entity4_id": "4th underlying climate entity id",
"ac_mode": "Use the Air Conditioning (AC) mode", "ac_mode": "Use the Air Conditioning (AC) mode",
"valve_entity_id": "1st valve number entity id",
"valve_entity2_id": "2nd valve number entity id",
"valve_entity3_id": "3rd valve number entity id",
"valve_entity4_id": "4th valve number entity id",
"auto_regulation_mode": "Auto adjustment of the target temperature", "auto_regulation_mode": "Auto adjustment of the target temperature",
"auto_regulation_dtemp": "The threshold in ° (or % for valve) under which the temperature change will not be sent", "auto_regulation_dtemp": "The threshold in ° (or % for valve) under which the temperature change will not be sent",
"auto_regulation_periode_min": "Duration in minutes between two regulation update", "auto_regulation_periode_min": "Duration in minutes between two regulation update",
@@ -309,21 +287,10 @@
"title": "Entities - {name}", "title": "Entities - {name}",
"description": "Linked entities attributes", "description": "Linked entities attributes",
"data": { "data": {
"heater_entity_id": "1st heater switch", "underlying_entity_ids": "The device(s) to be controlled",
"heater_entity2_id": "2nd heater switch",
"heater_entity3_id": "3rd heater switch",
"heater_entity4_id": "4th heater switch",
"heater_keep_alive": "Switch keep-alive interval in seconds", "heater_keep_alive": "Switch keep-alive interval in seconds",
"proportional_function": "Algorithm", "proportional_function": "Algorithm",
"climate_entity_id": "1st underlying climate",
"climate_entity2_id": "2nd underlying climate",
"climate_entity3_id": "3rd underlying climate",
"climate_entity4_id": "4th underlying climate",
"ac_mode": "AC mode", "ac_mode": "AC mode",
"valve_entity_id": "1st valve number",
"valve_entity2_id": "2nd valve number",
"valve_entity3_id": "3rd valve number",
"valve_entity4_id": "4th valve number",
"auto_regulation_mode": "Self-regulation", "auto_regulation_mode": "Self-regulation",
"auto_regulation_dtemp": "Regulation threshold", "auto_regulation_dtemp": "Regulation threshold",
"auto_regulation_periode_min": "Regulation minimum period", "auto_regulation_periode_min": "Regulation minimum period",
@@ -332,21 +299,10 @@
"auto_fan_mode": "Auto fan mode" "auto_fan_mode": "Auto fan mode"
}, },
"data_description": { "data_description": {
"heater_entity_id": "Mandatory heater entity id", "underlying_entity_ids": "The device(s) to be controlled - 1 is required",
"heater_entity2_id": "Optional 2nd Heater entity id. Leave empty if not used",
"heater_entity3_id": "Optional 3rd Heater entity id. Leave empty if not used",
"heater_entity4_id": "Optional 4th Heater entity id. Leave empty if not used",
"heater_keep_alive": "Optional heater switch state refresh interval. Leave empty if not required.", "heater_keep_alive": "Optional heater switch state refresh interval. Leave empty if not required.",
"proportional_function": "Algorithm to use (TPI is the only one for now)", "proportional_function": "Algorithm to use (TPI is the only one for now)",
"climate_entity_id": "Underlying climate entity id",
"climate_entity2_id": "2nd underlying climate entity id",
"climate_entity3_id": "3rd underlying climate entity id",
"climate_entity4_id": "4th underlying climate entity id",
"ac_mode": "Use the Air Conditioning (AC) mode", "ac_mode": "Use the Air Conditioning (AC) mode",
"valve_entity_id": "1st valve number entity id",
"valve_entity2_id": "2nd valve number entity id",
"valve_entity3_id": "3rd valve number entity id",
"valve_entity4_id": "4th valve number entity id",
"auto_regulation_mode": "Auto adjustment of the target temperature", "auto_regulation_mode": "Auto adjustment of the target temperature",
"auto_regulation_dtemp": "The threshold in ° (or % for valve) under which the temperature change will not be sent", "auto_regulation_dtemp": "The threshold in ° (or % for valve) under which the temperature change will not be sent",
"auto_regulation_periode_min": "Duration in minutes between two regulation update", "auto_regulation_periode_min": "Duration in minutes between two regulation update",
@@ -536,7 +492,8 @@
"state": { "state": {
"power": "Shedding", "power": "Shedding",
"security": "Safety", "security": "Safety",
"none": "Manual" "none": "Manual",
"frost": "Frost"
} }
} }
} }
@@ -590,4 +547,4 @@
} }
} }
} }
} }
@@ -554,7 +554,8 @@
"state": { "state": {
"power": "Délestage", "power": "Délestage",
"security": "Sécurité", "security": "Sécurité",
"none": "Manuel" "none": "Manuel",
"frost": "Hors Gel"
} }
} }
} }
@@ -364,7 +364,8 @@
"state": { "state": {
"power": "Ripartizione", "power": "Ripartizione",
"security": "Sicurezza", "security": "Sicurezza",
"none": "Manuale" "none": "Manuale",
"frost": "Gelo"
} }
} }
} }
+7 -10
View File
@@ -74,7 +74,7 @@ MOCK_TH_OVER_SWITCH_CENTRAL_MAIN_CONFIG = {
} }
MOCK_TH_OVER_SWITCH_TYPE_CONFIG = { MOCK_TH_OVER_SWITCH_TYPE_CONFIG = {
CONF_HEATER: "switch.mock_switch", CONF_UNDERLYING_LIST: ["switch.mock_switch"],
CONF_HEATER_KEEP_ALIVE: 0, CONF_HEATER_KEEP_ALIVE: 0,
CONF_PROP_FUNCTION: PROPORTIONAL_FUNCTION_TPI, CONF_PROP_FUNCTION: PROPORTIONAL_FUNCTION_TPI,
CONF_AC_MODE: False, CONF_AC_MODE: False,
@@ -82,17 +82,14 @@ MOCK_TH_OVER_SWITCH_TYPE_CONFIG = {
} }
MOCK_TH_OVER_SWITCH_AC_TYPE_CONFIG = { MOCK_TH_OVER_SWITCH_AC_TYPE_CONFIG = {
CONF_HEATER: "switch.mock_air_conditioner", CONF_UNDERLYING_LIST: ["switch.mock_air_conditioner"],
CONF_PROP_FUNCTION: PROPORTIONAL_FUNCTION_TPI, CONF_PROP_FUNCTION: PROPORTIONAL_FUNCTION_TPI,
CONF_AC_MODE: True, CONF_AC_MODE: True,
CONF_INVERSE_SWITCH: False, CONF_INVERSE_SWITCH: False,
} }
MOCK_TH_OVER_4SWITCH_TYPE_CONFIG = { MOCK_TH_OVER_4SWITCH_TYPE_CONFIG = {
CONF_HEATER: "switch.mock_4switch0", CONF_UNDERLYING_LIST: ["switch.mock_4switch0", "switch.mock_4switch1","switch.mock_4switch2","switch.mock_4switch3"],
CONF_HEATER_2: "switch.mock_4switch1",
CONF_HEATER_3: "switch.mock_4switch2",
CONF_HEATER_4: "switch.mock_4switch3",
CONF_HEATER_KEEP_ALIVE: 0, CONF_HEATER_KEEP_ALIVE: 0,
CONF_PROP_FUNCTION: PROPORTIONAL_FUNCTION_TPI, CONF_PROP_FUNCTION: PROPORTIONAL_FUNCTION_TPI,
CONF_AC_MODE: False, CONF_AC_MODE: False,
@@ -105,7 +102,7 @@ MOCK_TH_OVER_SWITCH_TPI_CONFIG = {
} }
MOCK_TH_OVER_CLIMATE_TYPE_CONFIG = { MOCK_TH_OVER_CLIMATE_TYPE_CONFIG = {
CONF_CLIMATE: "climate.mock_climate", CONF_UNDERLYING_LIST: ["climate.mock_climate"],
CONF_AC_MODE: False, CONF_AC_MODE: False,
CONF_AUTO_REGULATION_MODE: CONF_AUTO_REGULATION_STRONG, CONF_AUTO_REGULATION_MODE: CONF_AUTO_REGULATION_STRONG,
CONF_AUTO_REGULATION_DTEMP: 0.5, CONF_AUTO_REGULATION_DTEMP: 0.5,
@@ -115,7 +112,7 @@ MOCK_TH_OVER_CLIMATE_TYPE_CONFIG = {
} }
MOCK_TH_OVER_CLIMATE_TYPE_USE_DEVICE_TEMP_CONFIG = { MOCK_TH_OVER_CLIMATE_TYPE_USE_DEVICE_TEMP_CONFIG = {
CONF_CLIMATE: "climate.mock_climate", CONF_UNDERLYING_LIST: ["climate.mock_climate"],
CONF_AC_MODE: False, CONF_AC_MODE: False,
CONF_AUTO_REGULATION_MODE: CONF_AUTO_REGULATION_STRONG, CONF_AUTO_REGULATION_MODE: CONF_AUTO_REGULATION_STRONG,
CONF_AUTO_REGULATION_DTEMP: 0.1, CONF_AUTO_REGULATION_DTEMP: 0.1,
@@ -125,13 +122,13 @@ MOCK_TH_OVER_CLIMATE_TYPE_USE_DEVICE_TEMP_CONFIG = {
} }
MOCK_TH_OVER_CLIMATE_TYPE_NOT_REGULATED_CONFIG = { MOCK_TH_OVER_CLIMATE_TYPE_NOT_REGULATED_CONFIG = {
CONF_CLIMATE: "climate.mock_climate", CONF_UNDERLYING_LIST: ["climate.mock_climate"],
CONF_AC_MODE: False, CONF_AC_MODE: False,
CONF_AUTO_REGULATION_MODE: CONF_AUTO_REGULATION_NONE, CONF_AUTO_REGULATION_MODE: CONF_AUTO_REGULATION_NONE,
} }
MOCK_TH_OVER_CLIMATE_TYPE_AC_CONFIG = { MOCK_TH_OVER_CLIMATE_TYPE_AC_CONFIG = {
CONF_CLIMATE: "climate.mock_climate", CONF_UNDERLYING_LIST: ["climate.mock_climate"],
CONF_AC_MODE: True, CONF_AC_MODE: True,
CONF_AUTO_REGULATION_MODE: CONF_AUTO_REGULATION_STRONG, CONF_AUTO_REGULATION_MODE: CONF_AUTO_REGULATION_STRONG,
CONF_AUTO_REGULATION_DTEMP: 0.5, CONF_AUTO_REGULATION_DTEMP: 0.5,
+15 -1
View File
@@ -444,6 +444,8 @@ async def test_auto_start_stop_medium_heat_vtherm(
"target_temperature": 19.0, "target_temperature": 19.0,
"current_temperature": 21.0, "current_temperature": 21.0,
"temperature_slope": 0.167, "temperature_slope": 0.167,
"accumulated_error": -5,
"accumulated_error_threshold": 5,
}, },
) )
] ]
@@ -507,6 +509,8 @@ async def test_auto_start_stop_medium_heat_vtherm(
"target_temperature": 19.0, "target_temperature": 19.0,
"current_temperature": 18.0, "current_temperature": 18.0,
"temperature_slope": -0.034, "temperature_slope": -0.034,
"accumulated_error": 5,
"accumulated_error_threshold": 5,
}, },
) )
] ]
@@ -569,7 +573,7 @@ async def test_auto_start_stop_fast_ac_vtherm(
CONF_USE_AUTO_START_STOP_FEATURE: True, CONF_USE_AUTO_START_STOP_FEATURE: True,
CONF_USE_PRESENCE_FEATURE: True, CONF_USE_PRESENCE_FEATURE: True,
CONF_PRESENCE_SENSOR: "binary_sensor.presence_sensor", CONF_PRESENCE_SENSOR: "binary_sensor.presence_sensor",
CONF_CLIMATE: "climate.mock_climate", CONF_UNDERLYING_LIST: ["climate.mock_climate"],
CONF_MINIMAL_ACTIVATION_DELAY: 30, CONF_MINIMAL_ACTIVATION_DELAY: 30,
CONF_SECURITY_DELAY_MIN: 5, CONF_SECURITY_DELAY_MIN: 5,
CONF_SECURITY_MIN_ON_PERCENT: 0.3, CONF_SECURITY_MIN_ON_PERCENT: 0.3,
@@ -672,6 +676,8 @@ async def test_auto_start_stop_fast_ac_vtherm(
"target_temperature": 25.0, "target_temperature": 25.0,
"current_temperature": 23.0, "current_temperature": 23.0,
"temperature_slope": -0.28, "temperature_slope": -0.28,
"accumulated_error": 2,
"accumulated_error_threshold": 2,
}, },
) )
] ]
@@ -734,6 +740,8 @@ async def test_auto_start_stop_fast_ac_vtherm(
"target_temperature": 25.0, "target_temperature": 25.0,
"current_temperature": 26.5, "current_temperature": 26.5,
"temperature_slope": 0.112, "temperature_slope": 0.112,
"accumulated_error": -2,
"accumulated_error_threshold": 2,
}, },
) )
] ]
@@ -881,6 +889,8 @@ async def test_auto_start_stop_medium_heat_vtherm_preset_change(
"target_temperature": 17.0, "target_temperature": 17.0,
"current_temperature": 19.0, "current_temperature": 19.0,
"temperature_slope": 0.3, "temperature_slope": 0.3,
"accumulated_error": -2,
"accumulated_error_threshold": 2,
}, },
) )
] ]
@@ -936,6 +946,8 @@ async def test_auto_start_stop_medium_heat_vtherm_preset_change(
"target_temperature": 21.0, "target_temperature": 21.0,
"current_temperature": 17.0, "current_temperature": 17.0,
"temperature_slope": -0.087, "temperature_slope": -0.087,
"accumulated_error": 2,
"accumulated_error_threshold": 2,
}, },
) )
] ]
@@ -1445,6 +1457,8 @@ async def test_auto_start_stop_fast_heat_window_mixed(
"target_temperature": 19.0, "target_temperature": 19.0,
"current_temperature": 21.0, "current_temperature": 21.0,
"temperature_slope": 0.214, "temperature_slope": 0.214,
"accumulated_error": -2,
"accumulated_error_threshold": 2,
}, },
), ),
] ]
+7 -10
View File
@@ -87,7 +87,7 @@ async def test_user_config_flow_over_switch(
result = await hass.config_entries.flow.async_configure( result = await hass.config_entries.flow.async_configure(
result["flow_id"], result["flow_id"],
user_input={ user_input={
CONF_HEATER: "switch.mock_switch", CONF_UNDERLYING_LIST: ["switch.mock_switch"],
CONF_HEATER_KEEP_ALIVE: 0, CONF_HEATER_KEEP_ALIVE: 0,
CONF_PROP_FUNCTION: PROPORTIONAL_FUNCTION_TPI, CONF_PROP_FUNCTION: PROPORTIONAL_FUNCTION_TPI,
CONF_AC_MODE: False, CONF_AC_MODE: False,
@@ -292,7 +292,7 @@ async def test_user_config_flow_over_switch(
) )
assert result["result"] assert result["result"]
assert result["result"].domain == DOMAIN assert result["result"].domain == DOMAIN
assert result["result"].version == 1 assert result["result"].version == 2
assert result["result"].title == "TheOverSwitchMockName" assert result["result"].title == "TheOverSwitchMockName"
assert isinstance(result["result"], ConfigEntry) assert isinstance(result["result"], ConfigEntry)
@@ -379,7 +379,7 @@ async def test_user_config_flow_over_climate(
result = await hass.config_entries.flow.async_configure( result = await hass.config_entries.flow.async_configure(
result["flow_id"], result["flow_id"],
user_input={ user_input={
CONF_CLIMATE: "climate.mock_climate", CONF_UNDERLYING_LIST: ["climate.mock_climate"],
CONF_AC_MODE: False, CONF_AC_MODE: False,
CONF_AUTO_REGULATION_MODE: CONF_AUTO_REGULATION_STRONG, CONF_AUTO_REGULATION_MODE: CONF_AUTO_REGULATION_STRONG,
CONF_AUTO_REGULATION_DTEMP: 0.5, CONF_AUTO_REGULATION_DTEMP: 0.5,
@@ -512,7 +512,7 @@ async def test_user_config_flow_over_climate(
} }
assert result["result"] assert result["result"]
assert result["result"].domain == DOMAIN assert result["result"].domain == DOMAIN
assert result["result"].version == 1 assert result["result"].version == 2
assert result["result"].title == "TheOverClimateMockName" assert result["result"].title == "TheOverClimateMockName"
assert isinstance(result["result"], ConfigEntry) assert isinstance(result["result"], ConfigEntry)
@@ -794,10 +794,7 @@ async def test_user_config_flow_over_4_switches(
} }
TYPE_CONFIG = { # pylint: disable=invalid-name TYPE_CONFIG = { # pylint: disable=invalid-name
CONF_HEATER: "switch.mock_switch1", CONF_UNDERLYING_LIST: ["switch.mock_switch1", "switch.mock_switch2", "switch.mock_switch3","switch.mock_switch4"],
CONF_HEATER_2: "switch.mock_switch2",
CONF_HEATER_3: "switch.mock_switch3",
CONF_HEATER_4: "switch.mock_switch4",
CONF_HEATER_KEEP_ALIVE: 0, CONF_HEATER_KEEP_ALIVE: 0,
CONF_PROP_FUNCTION: PROPORTIONAL_FUNCTION_TPI, CONF_PROP_FUNCTION: PROPORTIONAL_FUNCTION_TPI,
CONF_AC_MODE: False, CONF_AC_MODE: False,
@@ -1015,7 +1012,7 @@ async def test_user_config_flow_over_climate_auto_start_stop(
result = await hass.config_entries.flow.async_configure( result = await hass.config_entries.flow.async_configure(
result["flow_id"], result["flow_id"],
user_input={ user_input={
CONF_CLIMATE: "climate.mock_climate", CONF_UNDERLYING_LIST: ["climate.mock_climate"],
CONF_AC_MODE: False, CONF_AC_MODE: False,
CONF_AUTO_REGULATION_MODE: CONF_AUTO_REGULATION_STRONG, CONF_AUTO_REGULATION_MODE: CONF_AUTO_REGULATION_STRONG,
CONF_AUTO_REGULATION_DTEMP: 0.5, CONF_AUTO_REGULATION_DTEMP: 0.5,
@@ -1124,6 +1121,6 @@ async def test_user_config_flow_over_climate_auto_start_stop(
} }
assert result["result"] assert result["result"]
assert result["result"].domain == DOMAIN assert result["result"].domain == DOMAIN
assert result["result"].version == 1 assert result["result"].version == 2
assert result["result"].title == "TheOverClimateMockName" assert result["result"].title == "TheOverClimateMockName"
assert isinstance(result["result"], ConfigEntry) assert isinstance(result["result"], ConfigEntry)