Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
382f6f99c6 | ||
|
|
95c4aa8ae9 | ||
|
|
a6a47fde53 | ||
|
|
e08f51b4f2 |
@@ -21,7 +21,9 @@
|
||||
"ms-python.python",
|
||||
"github.vscode-pull-request-github",
|
||||
"ryanluker.vscode-coverage-gutters",
|
||||
"ms-python.vscode-pylance"
|
||||
"ms-python.black-formatter",
|
||||
"ms-python.pylint",
|
||||
"ferrierbenjamin.fold-unfold-all-icone"
|
||||
],
|
||||
// "mounts": [
|
||||
// "source=${localWorkspaceFolder}/.devcontainer/configuration.yaml,target=${localWorkspaceFolder}/config/www/community/,type=bind,consistency=cached",
|
||||
@@ -40,8 +42,7 @@
|
||||
// "terminal.integrated.shell.linux": "/bin/bash",
|
||||
"python.pythonPath": "/usr/bin/python3",
|
||||
"python.analysis.autoSearchPaths": true,
|
||||
"python.linting.pylintEnabled": true,
|
||||
"python.linting.enabled": true,
|
||||
"pylint.lintOnChange": false,
|
||||
"python.formatting.provider": "black",
|
||||
"python.formatting.blackPath": "/usr/local/py-utils/bin/black",
|
||||
"editor.formatOnPaste": false,
|
||||
|
||||
6
.vscode/settings.json
vendored
6
.vscode/settings.json
vendored
@@ -1,9 +1,9 @@
|
||||
{
|
||||
"[python]": {
|
||||
"editor.defaultFormatter": "ms-python.python"
|
||||
"editor.defaultFormatter": "ms-python.black-formatter",
|
||||
"editor.formatOnSave": true
|
||||
},
|
||||
"python.linting.pylintEnabled": true,
|
||||
"python.linting.enabled": true,
|
||||
"pylint.lintOnChange": false,
|
||||
"files.associations": {
|
||||
"*.yaml": "home-assistant"
|
||||
},
|
||||
|
||||
@@ -51,7 +51,7 @@
|
||||
- [Attributs personnalisés](#attributs-personnalisés)
|
||||
- [Quelques résultats](#quelques-résultats)
|
||||
- [Encore mieux](#encore-mieux)
|
||||
- [Bien mieux avec le Veersatile Thermostat UI Card](#bien-mieux-avec-le-veersatile-thermostat-ui-card)
|
||||
- [Bien mieux avec le Versatile Thermostat UI Card](#bien-mieux-avec-le-versatile-thermostat-ui-card)
|
||||
- [Encore mieux avec le composant Scheduler !](#encore-mieux-avec-le-composant-scheduler-)
|
||||
- [Encore bien mieux avec la custom:simple-thermostat front integration](#encore-bien-mieux-avec-la-customsimple-thermostat-front-integration)
|
||||
- [Toujours mieux avec Apex-chart pour régler votre thermostat](#toujours-mieux-avec-apex-chart-pour-régler-votre-thermostat)
|
||||
@@ -735,7 +735,7 @@ Enjoy !
|
||||
|
||||
# Encore mieux
|
||||
|
||||
## Bien mieux avec le Veersatile Thermostat UI Card
|
||||
## Bien mieux avec le Versatile Thermostat UI Card
|
||||
Une carte spéciale pour le Versatile Thermostat a été développée (sur la base du Better Thermostat). Elle est dispo ici [Versatile Thermostat UI Card](https://github.com/jmcollin78/versatile-thermostat-ui-card) et propose une vision moderne de tous les status du VTherm :
|
||||
|
||||

|
||||
|
||||
@@ -130,47 +130,50 @@ class BaseThermostat(ClimateEntity, RestoreEntity):
|
||||
_motion_state: bool
|
||||
_presence_state: bool
|
||||
_window_auto_state: bool
|
||||
#PR - Adding Window ByPass
|
||||
_window_bypass_state: bool
|
||||
_underlyings: list[UnderlyingEntity]
|
||||
_last_change_time: datetime
|
||||
|
||||
_entity_component_unrecorded_attributes = ClimateEntity._entity_component_unrecorded_attributes.union(frozenset(
|
||||
{
|
||||
"type",
|
||||
"eco_temp",
|
||||
"boost_temp",
|
||||
"comfort_temp",
|
||||
"eco_away_temp",
|
||||
"boost_away_temp",
|
||||
"comfort_away_temp",
|
||||
"power_temp",
|
||||
"ac_mode",
|
||||
"current_power_max",
|
||||
"saved_preset_mode",
|
||||
"saved_target_temp",
|
||||
"saved_hvac_mode",
|
||||
"security_delay_min",
|
||||
"security_min_on_percent",
|
||||
"security_default_on_percent",
|
||||
"last_temperature_datetime",
|
||||
"last_ext_temperature_datetime",
|
||||
"minimal_activation_delay_sec",
|
||||
"device_power",
|
||||
"mean_cycle_power",
|
||||
"last_update_datetime",
|
||||
"timezone",
|
||||
"window_sensor_entity_id",
|
||||
"window_delay_sec",
|
||||
"window_auto_open_threshold",
|
||||
"window_auto_close_threshold",
|
||||
"window_auto_max_duration",
|
||||
"motion_sensor_entity_id",
|
||||
"presence_sensor_entity_id",
|
||||
"power_sensor_entity_id",
|
||||
"max_power_sensor_entity_id",
|
||||
}
|
||||
))
|
||||
_entity_component_unrecorded_attributes = (
|
||||
ClimateEntity._entity_component_unrecorded_attributes.union(
|
||||
frozenset(
|
||||
{
|
||||
"type",
|
||||
"eco_temp",
|
||||
"boost_temp",
|
||||
"comfort_temp",
|
||||
"eco_away_temp",
|
||||
"boost_away_temp",
|
||||
"comfort_away_temp",
|
||||
"power_temp",
|
||||
"ac_mode",
|
||||
"current_power_max",
|
||||
"saved_preset_mode",
|
||||
"saved_target_temp",
|
||||
"saved_hvac_mode",
|
||||
"security_delay_min",
|
||||
"security_min_on_percent",
|
||||
"security_default_on_percent",
|
||||
"last_temperature_datetime",
|
||||
"last_ext_temperature_datetime",
|
||||
"minimal_activation_delay_sec",
|
||||
"device_power",
|
||||
"mean_cycle_power",
|
||||
"last_update_datetime",
|
||||
"timezone",
|
||||
"window_sensor_entity_id",
|
||||
"window_delay_sec",
|
||||
"window_auto_open_threshold",
|
||||
"window_auto_close_threshold",
|
||||
"window_auto_max_duration",
|
||||
"motion_sensor_entity_id",
|
||||
"presence_sensor_entity_id",
|
||||
"power_sensor_entity_id",
|
||||
"max_power_sensor_entity_id",
|
||||
}
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
def __init__(self, hass: HomeAssistant, unique_id, name, entry_infos) -> None:
|
||||
"""Initialize the thermostat."""
|
||||
@@ -621,7 +624,7 @@ class BaseThermostat(ClimateEntity, RestoreEntity):
|
||||
STATE_UNAVAILABLE,
|
||||
STATE_UNKNOWN,
|
||||
):
|
||||
self._window_state = (window_state.state == STATE_ON)
|
||||
self._window_state = window_state.state == STATE_ON
|
||||
_LOGGER.debug(
|
||||
"%s - Window state have been retrieved: %s",
|
||||
self,
|
||||
@@ -762,17 +765,17 @@ class BaseThermostat(ClimateEntity, RestoreEntity):
|
||||
|
||||
@property
|
||||
def is_over_climate(self) -> bool:
|
||||
""" True if the Thermostat is over_climate"""
|
||||
"""True if the Thermostat is over_climate"""
|
||||
return False
|
||||
|
||||
@property
|
||||
def is_over_switch(self) -> bool:
|
||||
""" True if the Thermostat is over_switch"""
|
||||
"""True if the Thermostat is over_switch"""
|
||||
return False
|
||||
|
||||
@property
|
||||
def is_over_valve(self) -> bool:
|
||||
""" True if the Thermostat is over_valve"""
|
||||
"""True if the Thermostat is over_valve"""
|
||||
return False
|
||||
|
||||
@property
|
||||
@@ -933,10 +936,7 @@ class BaseThermostat(ClimateEntity, RestoreEntity):
|
||||
if not self._device_power:
|
||||
return None
|
||||
|
||||
return float(
|
||||
self._device_power
|
||||
* self._prop_algorithm.on_percent
|
||||
)
|
||||
return float(self._device_power * self._prop_algorithm.on_percent)
|
||||
|
||||
@property
|
||||
def total_energy(self) -> float | None:
|
||||
@@ -963,7 +963,6 @@ class BaseThermostat(ClimateEntity, RestoreEntity):
|
||||
"""Get the window_auto_state"""
|
||||
return STATE_ON if self._window_auto_state else STATE_OFF
|
||||
|
||||
#PR - Adding Window ByPass
|
||||
@property
|
||||
def window_bypass_state(self) -> bool | None:
|
||||
"""Get the Window Bypass"""
|
||||
@@ -1219,7 +1218,7 @@ class BaseThermostat(ClimateEntity, RestoreEntity):
|
||||
|
||||
async def _async_internal_set_temperature(self, temperature):
|
||||
"""Set the target temperature and the target temperature of underlying climate if any
|
||||
For testing purpose you can pass an event_timestamp.
|
||||
For testing purpose you can pass an event_timestamp.
|
||||
"""
|
||||
self._target_temp = temperature
|
||||
return
|
||||
@@ -1307,7 +1306,7 @@ class BaseThermostat(ClimateEntity, RestoreEntity):
|
||||
_LOGGER.debug(
|
||||
"Window delay condition is not satisfied. Ignore window event"
|
||||
)
|
||||
self._window_state = (old_state.state == STATE_ON)
|
||||
self._window_state = old_state.state == STATE_ON
|
||||
return
|
||||
|
||||
_LOGGER.debug("%s - Window delay condition is satisfied", self)
|
||||
@@ -1318,13 +1317,14 @@ class BaseThermostat(ClimateEntity, RestoreEntity):
|
||||
_LOGGER.debug("%s - no change in window state. Forget the event")
|
||||
return
|
||||
|
||||
self._window_state = new_state.state == STATE_ON
|
||||
|
||||
self._window_state = (new_state.state == STATE_ON)
|
||||
|
||||
#PR - Adding Window ByPass
|
||||
# PR - Adding Window ByPass
|
||||
_LOGGER.debug("%s - Window ByPass is : %s", self, self._window_bypass_state)
|
||||
if self._window_bypass_state:
|
||||
_LOGGER.info("%s - Window ByPass is activated. Ignore window event", self)
|
||||
_LOGGER.info(
|
||||
"%s - Window ByPass is activated. Ignore window event", self
|
||||
)
|
||||
else:
|
||||
if not self._window_state:
|
||||
_LOGGER.info(
|
||||
@@ -1827,7 +1827,15 @@ class BaseThermostat(ClimateEntity, RestoreEntity):
|
||||
self._device_power,
|
||||
)
|
||||
|
||||
ret = (self._current_power + self._device_power) >= self._current_power_max
|
||||
if self.is_over_climate:
|
||||
power_consumption_max = self._device_power
|
||||
else:
|
||||
power_consumption_max = max(
|
||||
self._device_power / self.nb_underlying_entities,
|
||||
self._device_power * self._prop_algorithm.on_percent,
|
||||
)
|
||||
|
||||
ret = (self._current_power + power_consumption_max) >= self._current_power_max
|
||||
if not self._overpowering_state and ret and self._hvac_mode != HVACMode.OFF:
|
||||
_LOGGER.warning(
|
||||
"%s - overpowering is detected. Heater preset will be set to 'power'",
|
||||
@@ -1845,6 +1853,7 @@ class BaseThermostat(ClimateEntity, RestoreEntity):
|
||||
"current_power": self._current_power,
|
||||
"device_power": self._device_power,
|
||||
"current_power_max": self._current_power_max,
|
||||
"current_power_consumption": power_consumption_max,
|
||||
},
|
||||
)
|
||||
|
||||
@@ -2129,7 +2138,6 @@ class BaseThermostat(ClimateEntity, RestoreEntity):
|
||||
"overpowering_state": self.overpowering_state,
|
||||
"presence_state": self._presence_state,
|
||||
"window_auto_state": self.window_auto_state,
|
||||
#PR - Adding Window ByPass
|
||||
"window_bypass_state": self._window_bypass_state,
|
||||
"security_delay_min": self._security_delay_min,
|
||||
"security_min_on_percent": self._security_min_on_percent,
|
||||
@@ -2256,14 +2264,25 @@ class BaseThermostat(ClimateEntity, RestoreEntity):
|
||||
target:
|
||||
entity_id: climate.thermostat_1
|
||||
"""
|
||||
_LOGGER.info("%s - Calling service_set_window_bypass, window_bypass: %s", self, window_bypass)
|
||||
_LOGGER.info(
|
||||
"%s - Calling service_set_window_bypass, window_bypass: %s",
|
||||
self,
|
||||
window_bypass,
|
||||
)
|
||||
self._window_bypass_state = window_bypass
|
||||
if not self._window_bypass_state and self._window_state:
|
||||
_LOGGER.info("%s - Last window state was open & ByPass is now off. Set hvac_mode to '%s'", self, HVACMode.OFF)
|
||||
_LOGGER.info(
|
||||
"%s - Last window state was open & ByPass is now off. Set hvac_mode to '%s'",
|
||||
self,
|
||||
HVACMode.OFF,
|
||||
)
|
||||
self.save_hvac_mode()
|
||||
await self.async_set_hvac_mode(HVACMode.OFF)
|
||||
if self._window_bypass_state and self._window_state:
|
||||
_LOGGER.info("%s - Last window state was open & ByPass is now on. Set hvac_mode to last available mode", self)
|
||||
_LOGGER.info(
|
||||
"%s - Last window state was open & ByPass is now on. Set hvac_mode to last available mode",
|
||||
self,
|
||||
)
|
||||
await self.restore_hvac_mode(True)
|
||||
self.update_custom_attributes()
|
||||
|
||||
|
||||
@@ -348,6 +348,7 @@
|
||||
},
|
||||
"auto_regulation_mode": {
|
||||
"options": {
|
||||
"auto_regulation_slow": "Slow",
|
||||
"auto_regulation_strong": "Strong",
|
||||
"auto_regulation_medium": "Medium",
|
||||
"auto_regulation_light": "Light",
|
||||
|
||||
@@ -348,6 +348,7 @@
|
||||
},
|
||||
"auto_regulation_mode": {
|
||||
"options": {
|
||||
"auto_regulation_slow": "Slow",
|
||||
"auto_regulation_strong": "Strong",
|
||||
"auto_regulation_medium": "Medium",
|
||||
"auto_regulation_light": "Light",
|
||||
|
||||
@@ -349,6 +349,7 @@
|
||||
},
|
||||
"auto_regulation_mode": {
|
||||
"options": {
|
||||
"auto_regulation_slow": "Lente",
|
||||
"auto_regulation_strong": "Forte",
|
||||
"auto_regulation_medium": "Moyenne",
|
||||
"auto_regulation_light": "Légère",
|
||||
|
||||
@@ -30,15 +30,15 @@
|
||||
"heater_entity3_id": "Terzo riscaldatore",
|
||||
"heater_entity4_id": "Quarto riscaldatore",
|
||||
"proportional_function": "Algoritmo",
|
||||
"climate_entity_id": "Termostato sottostante",
|
||||
"climate_entity2_id": "Secundo termostato sottostante",
|
||||
"climate_entity3_id": "Terzo termostato sottostante",
|
||||
"climate_entity4_id": "Quarto termostato sottostante",
|
||||
"climate_entity_id": "Primo termostato",
|
||||
"climate_entity2_id": "Secondo termostato",
|
||||
"climate_entity3_id": "Terzo termostato",
|
||||
"climate_entity4_id": "Quarto termostato",
|
||||
"ac_mode": "AC mode ?",
|
||||
"valve_entity_id": "Primo valvola numero",
|
||||
"valve_entity2_id": "Secondo valvola numero",
|
||||
"valve_entity3_id": "Terzo valvola numero",
|
||||
"valve_entity4_id": "Quarto valvola numero",
|
||||
"valve_entity_id": "Prima valvola",
|
||||
"valve_entity2_id": "Seconda valvolao",
|
||||
"valve_entity3_id": "Terza valvola",
|
||||
"valve_entity4_id": "Quarta valvola",
|
||||
"auto_regulation_mode": "Autoregolamentazione",
|
||||
"inverse_switch_command": "Comando inverso"
|
||||
},
|
||||
@@ -48,15 +48,15 @@
|
||||
"heater_entity3_id": "Entity id del terzo riscaldatore facoltativo. Lasciare vuoto se non utilizzato",
|
||||
"heater_entity4_id": "Entity id del quarto riscaldatore facoltativo. Lasciare vuoto se non utilizzato",
|
||||
"proportional_function": "Algoritmo da utilizzare (il TPI per adesso è l'unico)",
|
||||
"climate_entity_id": "Entity id del termostato sottostante",
|
||||
"climate_entity2_id": "Entity id del secundo termostato sottostante",
|
||||
"climate_entity3_id": "Entity id del terzo termostato sottostante",
|
||||
"climate_entity4_id": "Entity id del quarto termostato sottostante",
|
||||
"climate_entity_id": "Entity id del primo termostato",
|
||||
"climate_entity2_id": "Entity id del secondo termostato",
|
||||
"climate_entity3_id": "Entity id del terzo termostato",
|
||||
"climate_entity4_id": "Entity id del quarto termostato",
|
||||
"ac_mode": "Utilizzare la modalità AC (Air Conditioned) ?",
|
||||
"valve_entity_id": "Entity id del primo valvola numero",
|
||||
"valve_entity2_id": "Entity id del secondo valvola numero",
|
||||
"valve_entity3_id": "Entity id del terzo valvola numero",
|
||||
"valve_entity4_id": "Entity id del quarto valvola numero",
|
||||
"valve_entity_id": "Entity id della prima valvola",
|
||||
"valve_entity2_id": "Entity id della seconda valvola",
|
||||
"valve_entity3_id": "Entity id della terza valvola",
|
||||
"valve_entity4_id": "Entity id della quarta valvola",
|
||||
"auto_regulation_mode": "Regolazione automatica della temperatura target",
|
||||
"inverse_switch_command": "Inverte il controllo dell'interruttore per un'installazione con filo pilota e diodo"
|
||||
}
|
||||
@@ -188,15 +188,15 @@
|
||||
"heater_entity3_id": "Terzo riscaldatore",
|
||||
"heater_entity4_id": "Quarto riscaldatore",
|
||||
"proportional_function": "Algoritmo",
|
||||
"climate_entity_id": "Termostato sottostante",
|
||||
"climate_entity2_id": "Secundo termostato sottostante",
|
||||
"climate_entity3_id": "Terzo termostato sottostante",
|
||||
"climate_entity4_id": "Quarto termostato sottostante",
|
||||
"climate_entity_id": "Primo termostato",
|
||||
"climate_entity2_id": "Secondo termostato",
|
||||
"climate_entity3_id": "Terzo termostato",
|
||||
"climate_entity4_id": "Quarto termostato",
|
||||
"ac_mode": "AC mode ?",
|
||||
"valve_entity_id": "Primo valvola numero",
|
||||
"valve_entity2_id": "Secondo valvola numero",
|
||||
"valve_entity3_id": "Terzo valvola numero",
|
||||
"valve_entity4_id": "Quarto valvola numero",
|
||||
"valve_entity_id": "Prima valvola",
|
||||
"valve_entity2_id": "Seconda valvola",
|
||||
"valve_entity3_id": "Terza valvola",
|
||||
"valve_entity4_id": "Quarta valvola",
|
||||
"auto_regulation_mode": "Autoregolamentazione",
|
||||
"inverse_switch_command": "Comando inverso"
|
||||
},
|
||||
@@ -206,15 +206,15 @@
|
||||
"heater_entity3_id": "Entity id del terzo riscaldatore facoltativo. Lasciare vuoto se non utilizzato",
|
||||
"heater_entity4_id": "Entity id del quarto riscaldatore facoltativo. Lasciare vuoto se non utilizzato",
|
||||
"proportional_function": "Algoritmo da utilizzare (il TPI per adesso è l'unico)",
|
||||
"climate_entity_id": "Entity id del termostato sottostante",
|
||||
"climate_entity2_id": "Entity id del secundo termostato sottostante",
|
||||
"climate_entity3_id": "Entity id del terzo termostato sottostante",
|
||||
"climate_entity4_id": "Entity id del quarto termostato sottostante",
|
||||
"climate_entity_id": "Entity id del primo termostato",
|
||||
"climate_entity2_id": "Entity id del secondo termostato",
|
||||
"climate_entity3_id": "Entity id del terzo termostato",
|
||||
"climate_entity4_id": "Entity id del quarto termostato",
|
||||
"ac_mode": "Utilizzare la modalità AC (Air Conditioned) ?",
|
||||
"valve_entity_id": "Entity id del primo valvola numero",
|
||||
"valve_entity2_id": "Entity id del secondo valvola numero",
|
||||
"valve_entity3_id": "Entity id del terzo valvola numero",
|
||||
"valve_entity4_id": "Entity id del quarto valvola numero",
|
||||
"valve_entity_id": "Entity id della prima valvola",
|
||||
"valve_entity2_id": "Entity id della seconda valvola",
|
||||
"valve_entity3_id": "Entity id della terza valvola",
|
||||
"valve_entity4_id": "Entity id della quarta valvola",
|
||||
"auto_regulation_mode": "Autoregolamentazione",
|
||||
"inverse_switch_command": "Inverte il controllo dell'interruttore per un'installazione con filo pilota e diodo"
|
||||
}
|
||||
@@ -252,9 +252,9 @@
|
||||
"data_description": {
|
||||
"window_sensor_entity_id": "Lasciare vuoto se non deve essere utilizzato alcun sensore finestra",
|
||||
"window_delay": "Ritardo in secondi prima che il rilevamento del sensore sia preso in considerazione",
|
||||
"window_auto_open_threshold": "Valore consigliato: tra 0.05 e 0.1. Lasciare vuoto se il rilevamento automatico della finestra aperta non è utilizzato",
|
||||
"window_auto_close_threshold": "Valore consigliato: 0. Lasciare vuoto se il rilevamento automatico della finestra aperta non è utilizzato",
|
||||
"window_auto_max_duration": "Valore consigliato: 60 (un'ora). Lasciare vuoto se il rilevamento automatico della finestra aperta non è utilizzato"
|
||||
"window_auto_open_threshold": "Valore consigliato: tra 0.05 e 0.1 - Lasciare vuoto se il rilevamento automatico della finestra aperta non è utilizzato",
|
||||
"window_auto_close_threshold": "Valore consigliato: 0 - Lasciare vuoto se il rilevamento automatico della finestra aperta non è utilizzato",
|
||||
"window_auto_max_duration": "Valore consigliato: 60 minuti. Lasciare vuoto se il rilevamento automatico della finestra aperta non è utilizzato"
|
||||
}
|
||||
},
|
||||
"motion": {
|
||||
@@ -320,12 +320,13 @@
|
||||
"thermostat_type": {
|
||||
"options": {
|
||||
"thermostat_over_switch": "Termostato su un interruttore",
|
||||
"thermostat_over_climate": "Termostato sopra un altro termostato",
|
||||
"thermostat_over_valve": "Thermostato su una valvola"
|
||||
"thermostat_over_climate": "Termostato su un climatizzatore",
|
||||
"thermostat_over_valve": "Termostato su una valvola"
|
||||
}
|
||||
},
|
||||
"auto_regulation_mode": {
|
||||
"options": {
|
||||
"auto_regulation_slow": "Lento",
|
||||
"auto_regulation_strong": "Forte",
|
||||
"auto_regulation_medium": "Media",
|
||||
"auto_regulation_light": "Leggera",
|
||||
|
||||
@@ -348,6 +348,7 @@
|
||||
},
|
||||
"auto_regulation_mode": {
|
||||
"options": {
|
||||
"auto_regulation_slow": "Slow",
|
||||
"auto_regulation_strong": "Strong",
|
||||
"auto_regulation_medium": "Medium",
|
||||
"auto_regulation_light": "Light",
|
||||
|
||||
@@ -163,7 +163,9 @@ async def test_one_switch_cycle(
|
||||
# assert entity.underlying_entity(0)._should_relaunch_control_heating is True
|
||||
|
||||
# Simulate the relaunch
|
||||
await entity.underlying_entity(0)._turn_on_later( # pylint: disable=protected-access
|
||||
await entity.underlying_entity(
|
||||
0
|
||||
)._turn_on_later( # pylint: disable=protected-access
|
||||
None
|
||||
)
|
||||
# wait restart
|
||||
@@ -184,7 +186,9 @@ async def test_one_switch_cycle(
|
||||
"custom_components.versatile_thermostat.underlyings.UnderlyingSwitch.is_device_active",
|
||||
return_value=True,
|
||||
) as mock_device_active:
|
||||
await entity.underlying_entity(0)._turn_off_later( # pylint: disable=protected-access
|
||||
await entity.underlying_entity(
|
||||
0
|
||||
)._turn_off_later( # pylint: disable=protected-access
|
||||
None
|
||||
)
|
||||
|
||||
@@ -207,7 +211,9 @@ async def test_one_switch_cycle(
|
||||
"custom_components.versatile_thermostat.underlyings.UnderlyingSwitch.is_device_active",
|
||||
return_value=True,
|
||||
) as mock_device_active:
|
||||
await entity.underlying_entity(0)._turn_on_later( # pylint: disable=protected-access
|
||||
await entity.underlying_entity(
|
||||
0
|
||||
)._turn_on_later( # pylint: disable=protected-access
|
||||
None
|
||||
)
|
||||
|
||||
@@ -591,3 +597,139 @@ async def test_multiple_climates_underlying_changes(
|
||||
assert entity.hvac_mode == HVACMode.HEAT
|
||||
assert entity.hvac_action == HVACAction.IDLE
|
||||
assert entity.is_device_active is False # pylint: disable=protected-access
|
||||
|
||||
|
||||
@pytest.mark.parametrize("expected_lingering_tasks", [True])
|
||||
@pytest.mark.parametrize("expected_lingering_timers", [True])
|
||||
async def test_multiple_switch_power_management(
|
||||
hass: HomeAssistant, skip_hass_states_is_state
|
||||
):
|
||||
"""Test the Power management"""
|
||||
|
||||
entry = MockConfigEntry(
|
||||
domain=DOMAIN,
|
||||
title="TheOverSwitchMockName",
|
||||
unique_id="uniqueId",
|
||||
data={
|
||||
CONF_NAME: "TheOver4SwitchMockName",
|
||||
CONF_THERMOSTAT_TYPE: CONF_THERMOSTAT_SWITCH,
|
||||
CONF_TEMP_SENSOR: "sensor.mock_temp_sensor",
|
||||
CONF_EXTERNAL_TEMP_SENSOR: "sensor.mock_ext_temp_sensor",
|
||||
CONF_CYCLE_MIN: 8,
|
||||
CONF_TEMP_MIN: 15,
|
||||
CONF_TEMP_MAX: 30,
|
||||
"eco_temp": 17,
|
||||
"comfort_temp": 18,
|
||||
"boost_temp": 19,
|
||||
CONF_USE_WINDOW_FEATURE: False,
|
||||
CONF_USE_MOTION_FEATURE: False,
|
||||
CONF_USE_POWER_FEATURE: True,
|
||||
CONF_USE_PRESENCE_FEATURE: False,
|
||||
CONF_HEATER: "switch.mock_switch1",
|
||||
CONF_HEATER_2: "switch.mock_switch2",
|
||||
CONF_HEATER_3: "switch.mock_switch3",
|
||||
CONF_HEATER_4: "switch.mock_switch4",
|
||||
CONF_MINIMAL_ACTIVATION_DELAY: 30,
|
||||
CONF_SECURITY_DELAY_MIN: 5,
|
||||
CONF_SECURITY_MIN_ON_PERCENT: 0.3,
|
||||
CONF_PROP_FUNCTION: PROPORTIONAL_FUNCTION_TPI,
|
||||
CONF_TPI_COEF_INT: 0.3,
|
||||
CONF_TPI_COEF_EXT: 0.01,
|
||||
CONF_POWER_SENSOR: "sensor.mock_power_sensor",
|
||||
CONF_MAX_POWER_SENSOR: "sensor.mock_power_max_sensor",
|
||||
CONF_DEVICE_POWER: 100,
|
||||
CONF_PRESET_POWER: 12,
|
||||
},
|
||||
)
|
||||
|
||||
entity: BaseThermostat = await create_thermostat(
|
||||
hass, entry, "climate.theover4switchmockname"
|
||||
)
|
||||
assert entity
|
||||
assert entity.is_over_climate is False
|
||||
assert entity.nb_underlying_entities == 4
|
||||
|
||||
tpi_algo = entity._prop_algorithm
|
||||
assert tpi_algo
|
||||
|
||||
await entity.async_set_hvac_mode(HVACMode.HEAT)
|
||||
await entity.async_set_preset_mode(PRESET_BOOST)
|
||||
assert entity.hvac_mode is HVACMode.HEAT
|
||||
assert entity.preset_mode is PRESET_BOOST
|
||||
assert entity.overpowering_state is None
|
||||
assert entity.target_temperature == 19
|
||||
|
||||
# 1. Send power mesurement
|
||||
await send_power_change_event(entity, 50, datetime.now())
|
||||
# Send power max mesurement
|
||||
await send_max_power_change_event(entity, 300, datetime.now())
|
||||
assert await entity.check_overpowering() is False
|
||||
# All configuration is complete and power is < power_max
|
||||
assert entity.preset_mode is PRESET_BOOST
|
||||
assert entity.overpowering_state is False
|
||||
|
||||
# 2. Send power max mesurement too low and HVACMode is on
|
||||
with patch(
|
||||
"custom_components.versatile_thermostat.base_thermostat.BaseThermostat.send_event"
|
||||
) as mock_send_event, patch(
|
||||
"custom_components.versatile_thermostat.underlyings.UnderlyingSwitch.turn_on"
|
||||
) as mock_heater_on, patch(
|
||||
"custom_components.versatile_thermostat.underlyings.UnderlyingSwitch.turn_off"
|
||||
) as mock_heater_off:
|
||||
# 100 of the device / 4 -> 25, current power 50 so max is 75
|
||||
await send_max_power_change_event(entity, 74, datetime.now())
|
||||
assert await entity.check_overpowering() is True
|
||||
# All configuration is complete and power is > power_max we switch to POWER preset
|
||||
assert entity.preset_mode is PRESET_POWER
|
||||
assert entity.overpowering_state is True
|
||||
assert entity.target_temperature == 12
|
||||
|
||||
assert mock_send_event.call_count == 2
|
||||
mock_send_event.assert_has_calls(
|
||||
[
|
||||
call.send_event(EventType.PRESET_EVENT, {"preset": PRESET_POWER}),
|
||||
call.send_event(
|
||||
EventType.POWER_EVENT,
|
||||
{
|
||||
"type": "start",
|
||||
"current_power": 50,
|
||||
"device_power": 100,
|
||||
"current_power_max": 74,
|
||||
"current_power_consumption": 25.0,
|
||||
},
|
||||
),
|
||||
],
|
||||
any_order=True,
|
||||
)
|
||||
assert mock_heater_on.call_count == 0
|
||||
assert mock_heater_off.call_count == 4 # The fourth are shutdown
|
||||
|
||||
# 3. change PRESET
|
||||
with patch(
|
||||
"custom_components.versatile_thermostat.base_thermostat.BaseThermostat.send_event"
|
||||
) as mock_send_event:
|
||||
await entity.async_set_preset_mode(PRESET_ECO)
|
||||
assert entity.preset_mode is PRESET_ECO
|
||||
# No change
|
||||
assert entity.overpowering_state is True
|
||||
|
||||
# 4. Send hugh power max mesurement to release overpowering
|
||||
with patch(
|
||||
"custom_components.versatile_thermostat.base_thermostat.BaseThermostat.send_event"
|
||||
) as mock_send_event, patch(
|
||||
"custom_components.versatile_thermostat.underlyings.UnderlyingSwitch.turn_on"
|
||||
) as mock_heater_on, patch(
|
||||
"custom_components.versatile_thermostat.underlyings.UnderlyingSwitch.turn_off"
|
||||
) as mock_heater_off:
|
||||
# 100 of the device / 4 -> 25, current power 50 so max is 75. With 150 no overheating
|
||||
await send_max_power_change_event(entity, 150, datetime.now())
|
||||
assert await entity.check_overpowering() is False
|
||||
# All configuration is complete and power is > power_max we switch to POWER preset
|
||||
assert entity.preset_mode is PRESET_ECO
|
||||
assert entity.overpowering_state is False
|
||||
assert entity.target_temperature == 17
|
||||
|
||||
assert (
|
||||
mock_heater_on.call_count == 0
|
||||
) # The fourth are not restarted because temperature is enought
|
||||
assert mock_heater_off.call_count == 0
|
||||
|
||||
@@ -4,8 +4,11 @@ from unittest.mock import patch, call
|
||||
from datetime import datetime, timedelta
|
||||
import logging
|
||||
|
||||
from custom_components.versatile_thermostat.thermostat_switch import ThermostatOverSwitch
|
||||
from custom_components.versatile_thermostat.thermostat_switch import (
|
||||
ThermostatOverSwitch,
|
||||
)
|
||||
from .commons import * # pylint: disable=wildcard-import, unused-wildcard-import
|
||||
|
||||
logging.getLogger().setLevel(logging.DEBUG)
|
||||
|
||||
|
||||
@@ -185,6 +188,7 @@ async def test_power_management_hvac_on(hass: HomeAssistant, skip_hass_states_is
|
||||
"current_power": 50,
|
||||
"device_power": 100,
|
||||
"current_power_max": 149,
|
||||
"current_power_consumption": 100.0,
|
||||
},
|
||||
),
|
||||
],
|
||||
|
||||
Reference in New Issue
Block a user