Compare commits

..

9 Commits

Author SHA1 Message Date
Jean-Marc Collin
d6ec7a86be issue #506 - Add some check to verify tpi algorithm parameters are correctly set. (#512)
Co-authored-by: Jean-Marc Collin <jean-marc.collin-extern@renault.com>
2024-09-29 09:54:39 +02:00
Jean-Marc Collin
a3f8715fe5 HA 2024.9.3 and issue 508 (#510)
* HA 2024.9.3 and issue 508

* Fix strings trailing spaces

---------

Co-authored-by: Jean-Marc Collin <jean-marc.collin-extern@renault.com>
2024-09-28 19:36:46 +02:00
domozer
a1a9a8bbab Update README-fr.md (#487)
Remplacement de "Awesome Thermostat" par "Versatile Thermostat" dans la section "Encore mieux avec le composant Scheduler !"

Idem pour le fichier README.md
2024-07-23 19:24:56 +02:00
Jean-Marc Collin
d5c5869276 Update settings 2024-07-17 06:50:01 +00:00
Jean-Marc Collin
c4b03f8c1e Update manifest.json 2024-07-07 16:49:22 +02:00
Paulo Ferreira de Castro
ac206a949f Fix Home Assistant deprecation warnings (EventType, helpers.service) (#484)
* Type hints: Replace deprecated helpers.typing.EventType with core.Event

* Replace deprecated use of hass.helpers.service.async_register_admin_service
2024-07-07 16:47:30 +02:00
Jean-Marc Collin
4bccb746b8 Release 6.2.8 2024-07-02 05:18:29 +00:00
Jean-Marc Collin
e999705286 Issue 474 - TPI in AC mode is wrong 2024-07-02 05:17:14 +00:00
Jean-Marc Collin
b4873bfd27 FIX issue_479 (#480)
Co-authored-by: Jean-Marc Collin <jean-marc.collin-extern@renault.com>
2024-07-02 07:04:47 +02:00
22 changed files with 432 additions and 94 deletions

View File

@@ -4,7 +4,6 @@
"editor.formatOnSave": true, "editor.formatOnSave": true,
"editor.formatOnSaveMode": "modifications" "editor.formatOnSaveMode": "modifications"
}, },
"pylint.lintOnChange": false,
"files.associations": { "files.associations": {
"*.yaml": "home-assistant" "*.yaml": "home-assistant"
}, },

View File

@@ -1202,9 +1202,9 @@ Une carte spéciale pour le Versatile Thermostat a été développée (sur la ba
## Encore mieux avec le composant Scheduler ! ## Encore mieux avec le composant Scheduler !
Afin de profiter de toute la puissance du Versatile Thermostat, je vous invite à l'utiliser avec https://github.com/nielsfaber/scheduler-component Afin de profiter de toute la puissance du Versatile Thermostat, je vous invite à l'utiliser avec https://github.com/nielsfaber/scheduler-component
En effet, le composant scheduler propose une gestion de la base climatique sur les modes prédéfinis. Cette fonctionnalité a un intérêt limité avec le thermostat générique mais elle devient très puissante avec le thermostat Awesome : En effet, le composant scheduler propose une gestion de la base climatique sur les modes prédéfinis. Cette fonctionnalité a un intérêt limité avec le thermostat générique mais elle devient très puissante avec le Versatile Thermostat :
À partir d'ici, je suppose que vous avez installé Awesome Thermostat et Scheduler Component. À partir d'ici, je suppose que vous avez installé Versatile Thermostat et Scheduler Component.
Dans Scheduler, ajoutez un planning : Dans Scheduler, ajoutez un planning :

View File

@@ -13,6 +13,7 @@ from homeassistant.const import SERVICE_RELOAD, EVENT_HOMEASSISTANT_STARTED
from homeassistant.config_entries import ConfigEntry, ConfigType from homeassistant.config_entries import ConfigEntry, ConfigType
from homeassistant.core import HomeAssistant, CoreState, callback from homeassistant.core import HomeAssistant, CoreState, callback
from homeassistant.helpers.service import async_register_admin_service
from .base_thermostat import BaseThermostat from .base_thermostat import BaseThermostat
@@ -115,7 +116,8 @@ async def async_setup(
else: else:
hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STARTED, _async_startup_internal) hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STARTED, _async_startup_internal)
hass.helpers.service.async_register_admin_service( async_register_admin_service(
hass,
DOMAIN, DOMAIN,
SERVICE_RELOAD, SERVICE_RELOAD,
_handle_reload, _handle_reload,

View File

@@ -22,7 +22,6 @@ from homeassistant.components.climate import ClimateEntity
from homeassistant.helpers.restore_state import RestoreEntity from homeassistant.helpers.restore_state import RestoreEntity
from homeassistant.config_entries import ConfigEntry from homeassistant.config_entries import ConfigEntry
from homeassistant.helpers.device_registry import DeviceInfo, DeviceEntryType from homeassistant.helpers.device_registry import DeviceInfo, DeviceEntryType
from homeassistant.helpers.typing import EventType as HASSEventType
from homeassistant.helpers.event import ( from homeassistant.helpers.event import (
async_track_state_change_event, async_track_state_change_event,
@@ -737,37 +736,37 @@ class BaseThermostat(ClimateEntity, RestoreEntity, Generic[T]):
) )
need_write_state = True need_write_state = True
# try to acquire window entity state # try to acquire window entity state
if self._window_sensor_entity_id: if self._window_sensor_entity_id:
window_state = self.hass.states.get(self._window_sensor_entity_id) window_state = self.hass.states.get(self._window_sensor_entity_id)
if window_state and window_state.state not in ( if window_state and window_state.state not in (
STATE_UNAVAILABLE, STATE_UNAVAILABLE,
STATE_UNKNOWN, STATE_UNKNOWN,
): ):
self._window_state = window_state.state == STATE_ON self._window_state = window_state.state == STATE_ON
_LOGGER.debug( _LOGGER.debug(
"%s - Window state have been retrieved: %s", "%s - Window state have been retrieved: %s",
self, self,
self._window_state, self._window_state,
) )
need_write_state = True need_write_state = True
# try to acquire motion entity state # try to acquire motion entity state
if self._motion_sensor_entity_id: if self._motion_sensor_entity_id:
motion_state = self.hass.states.get(self._motion_sensor_entity_id) motion_state = self.hass.states.get(self._motion_sensor_entity_id)
if motion_state and motion_state.state not in ( if motion_state and motion_state.state not in (
STATE_UNAVAILABLE, STATE_UNAVAILABLE,
STATE_UNKNOWN, STATE_UNKNOWN,
): ):
self._motion_state = motion_state.state self._motion_state = motion_state.state
_LOGGER.debug( _LOGGER.debug(
"%s - Motion state have been retrieved: %s", "%s - Motion state have been retrieved: %s",
self, self,
self._motion_state, self._motion_state,
) )
# recalculate the right target_temp in activity mode # recalculate the right target_temp in activity mode
await self._async_update_motion_temp() await self._async_update_motion_temp()
need_write_state = True need_write_state = True
if self._presence_on: if self._presence_on:
# try to acquire presence entity state # try to acquire presence entity state
@@ -1389,7 +1388,7 @@ class BaseThermostat(ClimateEntity, RestoreEntity, Generic[T]):
if self._motion_state == STATE_ON if self._motion_state == STATE_ON
else self._no_motion_preset else self._no_motion_preset
) )
if motion_preset in self._presets: if motion_preset in self._presets:
return self._presets[motion_preset] return self._presets[motion_preset]
else: else:
@@ -1654,7 +1653,7 @@ class BaseThermostat(ClimateEntity, RestoreEntity, Generic[T]):
_LOGGER.debug("%s - Motion delay condition is satisfied", self) _LOGGER.debug("%s - Motion delay condition is satisfied", self)
self._motion_state = new_state.state self._motion_state = new_state.state
if self._attr_preset_mode == PRESET_ACTIVITY: if self._attr_preset_mode == PRESET_ACTIVITY:
new_preset = ( new_preset = (
self._motion_preset self._motion_preset
if self._motion_state == STATE_ON if self._motion_state == STATE_ON
@@ -1790,7 +1789,7 @@ class BaseThermostat(ClimateEntity, RestoreEntity, Generic[T]):
_LOGGER.error("Unable to update external temperature from sensor: %s", ex) _LOGGER.error("Unable to update external temperature from sensor: %s", ex)
@callback @callback
async def _async_power_changed(self, event: HASSEventType[EventStateChangedData]): async def _async_power_changed(self, event: Event[EventStateChangedData]):
"""Handle power changes.""" """Handle power changes."""
_LOGGER.debug("Thermostat %s - Receive new Power event", self.name) _LOGGER.debug("Thermostat %s - Receive new Power event", self.name)
_LOGGER.debug(event) _LOGGER.debug(event)
@@ -1816,9 +1815,7 @@ class BaseThermostat(ClimateEntity, RestoreEntity, Generic[T]):
_LOGGER.error("Unable to update current_power from sensor: %s", ex) _LOGGER.error("Unable to update current_power from sensor: %s", ex)
@callback @callback
async def _async_max_power_changed( async def _async_max_power_changed(self, event: Event[EventStateChangedData]):
self, event: HASSEventType[EventStateChangedData]
):
"""Handle power max changes.""" """Handle power max changes."""
_LOGGER.debug("Thermostat %s - Receive new Power Max event", self.name) _LOGGER.debug("Thermostat %s - Receive new Power Max event", self.name)
_LOGGER.debug(event) _LOGGER.debug(event)
@@ -1843,9 +1840,7 @@ class BaseThermostat(ClimateEntity, RestoreEntity, Generic[T]):
_LOGGER.error("Unable to update current_power from sensor: %s", ex) _LOGGER.error("Unable to update current_power from sensor: %s", ex)
@callback @callback
async def _async_presence_changed( async def _async_presence_changed(self, event: Event[EventStateChangedData]):
self, event: HASSEventType[EventStateChangedData]
):
"""Handle presence changes.""" """Handle presence changes."""
new_state = event.data.get("new_state") new_state = event.data.get("new_state")
_LOGGER.info( _LOGGER.info(
@@ -1905,7 +1900,7 @@ class BaseThermostat(ClimateEntity, RestoreEntity, Generic[T]):
or self._attr_preset_mode != PRESET_ACTIVITY or self._attr_preset_mode != PRESET_ACTIVITY
): ):
return return
new_preset = ( new_preset = (
self._motion_preset self._motion_preset
if self._motion_state == STATE_ON if self._motion_state == STATE_ON
@@ -1916,13 +1911,13 @@ class BaseThermostat(ClimateEntity, RestoreEntity, Generic[T]):
self, self,
new_preset, new_preset,
) )
# We do not change the preset which is kept to ACTIVITY but only the target_temperature # We do not change the preset which is kept to ACTIVITY but only the target_temperature
# We take the presence into account # We take the presence into account
await self._async_internal_set_temperature( await self._async_internal_set_temperature(
self.find_preset_temp(new_preset) self.find_preset_temp(new_preset)
) )
_LOGGER.debug( _LOGGER.debug(
"%s - regarding motion, target_temp have been set to %.2f", "%s - regarding motion, target_temp have been set to %.2f",
self, self,

View File

@@ -14,6 +14,6 @@
"quality_scale": "silver", "quality_scale": "silver",
"requirements": [], "requirements": [],
"ssdp": [], "ssdp": [],
"version": "6.2.6", "version": "6.3.0",
"zeroconf": [] "zeroconf": []
} }

View File

@@ -14,6 +14,10 @@ PROPORTIONAL_MIN_DURATION_SEC = 10
FUNCTION_TYPE = [PROPORTIONAL_FUNCTION_ATAN, PROPORTIONAL_FUNCTION_LINEAR] FUNCTION_TYPE = [PROPORTIONAL_FUNCTION_ATAN, PROPORTIONAL_FUNCTION_LINEAR]
def is_number(value):
return isinstance(value, (int, float))
class PropAlgorithm: class PropAlgorithm:
"""This class aims to do all calculation of the Proportional alogorithm""" """This class aims to do all calculation of the Proportional alogorithm"""
@@ -36,6 +40,30 @@ class PropAlgorithm:
cycle_min, cycle_min,
minimal_activation_delay, minimal_activation_delay,
) )
# Issue 506 - check parameters
if (
vtherm_entity_id is None
or not is_number(tpi_coef_int)
or not is_number(tpi_coef_ext)
or not is_number(cycle_min)
or not is_number(minimal_activation_delay)
or function_type != PROPORTIONAL_FUNCTION_TPI
):
_LOGGER.error(
"%s - configuration is wrong. function_type=%s, entity_id is %s, tpi_coef_int is %s, tpi_coef_ext is %s, cycle_min is %s, minimal_activation_delay is %s",
vtherm_entity_id,
function_type,
vtherm_entity_id,
tpi_coef_int,
tpi_coef_ext,
cycle_min,
minimal_activation_delay,
)
raise TypeError(
f"TPI parameters are not set correctly. VTherm will not work as expected. Please reconfigure it correctly. See previous log for values"
)
self._vtherm_entity_id = vtherm_entity_id self._vtherm_entity_id = vtherm_entity_id
self._function = function_type self._function = function_type
self._tpi_coef_int = tpi_coef_int self._tpi_coef_int = tpi_coef_int
@@ -70,9 +98,9 @@ class PropAlgorithm:
if hvac_mode == HVACMode.COOL: if hvac_mode == HVACMode.COOL:
delta_temp = current_temp - target_temp delta_temp = current_temp - target_temp
delta_ext_temp = ( delta_ext_temp = (
ext_current_temp ext_current_temp - target_temp
if ext_current_temp is not None if ext_current_temp is not None
else 0 - target_temp else 0
) )
else: else:
delta_temp = target_temp - current_temp delta_temp = target_temp - current_temp

View File

@@ -90,7 +90,7 @@
"auto_regulation_periode_min": "Regulation minimum period", "auto_regulation_periode_min": "Regulation minimum period",
"auto_regulation_use_device_temp": "Use internal temperature of the underlying", "auto_regulation_use_device_temp": "Use internal temperature of the underlying",
"inverse_switch_command": "Inverse switch command", "inverse_switch_command": "Inverse switch command",
"auto_fan_mode": " Auto fan mode" "auto_fan_mode": "Auto fan mode"
}, },
"data_description": { "data_description": {
"heater_entity_id": "Mandatory heater entity id", "heater_entity_id": "Mandatory heater entity id",
@@ -113,7 +113,7 @@
"auto_regulation_periode_min": "Duration in minutes between two regulation update", "auto_regulation_periode_min": "Duration in minutes between two regulation update",
"auto_regulation_use_device_temp": "Use the eventual internal temperature sensor of the underlying to speedup the self-regulation", "auto_regulation_use_device_temp": "Use the eventual internal temperature sensor of the underlying to speedup the self-regulation",
"inverse_switch_command": "For switch with pilot wire and diode you may need to inverse the command", "inverse_switch_command": "For switch with pilot wire and diode you may need to inverse the command",
"auto_fan_mode": " Automatically activate fan when huge heating/cooling is necessary" "auto_fan_mode": "Automatically activate fan when huge heating/cooling is necessary"
} }
}, },
"tpi": { "tpi": {
@@ -325,7 +325,7 @@
"auto_regulation_periode_min": "Regulation minimum period", "auto_regulation_periode_min": "Regulation minimum period",
"auto_regulation_use_device_temp": "Use internal temperature of the underlying", "auto_regulation_use_device_temp": "Use internal temperature of the underlying",
"inverse_switch_command": "Inverse switch command", "inverse_switch_command": "Inverse switch command",
"auto_fan_mode": " Auto fan mode" "auto_fan_mode": "Auto fan mode"
}, },
"data_description": { "data_description": {
"heater_entity_id": "Mandatory heater entity id", "heater_entity_id": "Mandatory heater entity id",
@@ -348,7 +348,7 @@
"auto_regulation_periode_min": "Duration in minutes between two regulation update", "auto_regulation_periode_min": "Duration in minutes between two regulation update",
"auto_regulation_use_device_temp": "Use the eventual internal temperature sensor of the underlying to speedup the self-regulation", "auto_regulation_use_device_temp": "Use the eventual internal temperature sensor of the underlying to speedup the self-regulation",
"inverse_switch_command": "For switch with pilot wire and diode you may need to invert the command", "inverse_switch_command": "For switch with pilot wire and diode you may need to invert the command",
"auto_fan_mode": " Automatically activate fan when huge heating/cooling is necessary" "auto_fan_mode": "Automatically activate fan when huge heating/cooling is necessary"
} }
}, },
"tpi": { "tpi": {

View File

@@ -3,13 +3,12 @@
import logging import logging
from datetime import timedelta, datetime from datetime import timedelta, datetime
from homeassistant.core import HomeAssistant, State, callback from homeassistant.core import Event, HomeAssistant, State, callback
from homeassistant.helpers.event import ( from homeassistant.helpers.event import (
async_track_state_change_event, async_track_state_change_event,
async_track_time_interval, async_track_time_interval,
EventStateChangedData, EventStateChangedData,
) )
from homeassistant.helpers.typing import EventType as HASSEventType
from homeassistant.components.climate import ( from homeassistant.components.climate import (
HVACAction, HVACAction,
HVACMode, HVACMode,
@@ -600,7 +599,7 @@ class ThermostatOverClimate(BaseThermostat[UnderlyingClimate]):
) )
@callback @callback
async def _async_climate_changed(self, event: HASSEventType[EventStateChangedData]): async def _async_climate_changed(self, event: Event[EventStateChangedData]):
"""Handle unerdlying climate state changes. """Handle unerdlying climate state changes.
This method takes the underlying values and update the VTherm with them. This method takes the underlying values and update the VTherm with them.
To avoid loops (issues #121 #101 #95 #99), we discard the event if it is received To avoid loops (issues #121 #101 #95 #99), we discard the event if it is received

View File

@@ -2,12 +2,11 @@
""" A climate over switch classe """ """ A climate over switch classe """
import logging import logging
from homeassistant.core import callback from homeassistant.core import Event, callback
from homeassistant.helpers.event import ( from homeassistant.helpers.event import (
async_track_state_change_event, async_track_state_change_event,
EventStateChangedData, EventStateChangedData,
) )
from homeassistant.helpers.typing import EventType as HASSEventType
from homeassistant.components.climate import HVACMode from homeassistant.components.climate import HVACMode
from .const import ( from .const import (
@@ -212,7 +211,7 @@ class ThermostatOverSwitch(BaseThermostat[UnderlyingSwitch]):
) )
@callback @callback
def _async_switch_changed(self, event: HASSEventType[EventStateChangedData]): def _async_switch_changed(self, event: Event[EventStateChangedData]):
"""Handle heater switch state changes.""" """Handle heater switch state changes."""
new_state = event.data.get("new_state") new_state = event.data.get("new_state")
old_state = event.data.get("old_state") old_state = event.data.get("old_state")

View File

@@ -8,8 +8,7 @@ from homeassistant.helpers.event import (
async_track_time_interval, async_track_time_interval,
EventStateChangedData, EventStateChangedData,
) )
from homeassistant.helpers.typing import EventType as HASSEventType from homeassistant.core import Event, HomeAssistant, callback
from homeassistant.core import HomeAssistant, callback
from homeassistant.components.climate import HVACMode from homeassistant.components.climate import HVACMode
from .base_thermostat import BaseThermostat, ConfigData from .base_thermostat import BaseThermostat, ConfigData
@@ -149,7 +148,7 @@ class ThermostatOverValve(BaseThermostat[UnderlyingValve]): # pylint: disable=a
) )
@callback @callback
async def _async_valve_changed(self, event: HASSEventType[EventStateChangedData]): async def _async_valve_changed(self, event: Event[EventStateChangedData]):
"""Handle unerdlying valve state changes. """Handle unerdlying valve state changes.
This method just log the change. It changes nothing to avoid loops. This method just log the change. It changes nothing to avoid loops.
""" """

View File

@@ -43,7 +43,7 @@
"auto_regulation_dtemp": "Όριο ρύθμισης", "auto_regulation_dtemp": "Όριο ρύθμισης",
"auto_regulation_periode_min": "Ελάχιστη περίοδος ρύθμισης", "auto_regulation_periode_min": "Ελάχιστη περίοδος ρύθμισης",
"inverse_switch_command": "Αντίστροφη εντολή διακόπτη", "inverse_switch_command": "Αντίστροφη εντολή διακόπτη",
"auto_fan_mode": " Auto fan mode" "auto_fan_mode": "Auto fan mode"
}, },
"data_description": { "data_description": {
"heater_entity_id": "Υποχρεωτική ταυτότητα οντότητας θερμαντήρα", "heater_entity_id": "Υποχρεωτική ταυτότητα οντότητας θερμαντήρα",
@@ -64,7 +64,7 @@
"auto_regulation_dtemp": "Το όριο σε ° κάτω από το οποίο η αλλαγή θερμοκρασίας δεν θα αποστέλλεται", "auto_regulation_dtemp": "Το όριο σε ° κάτω από το οποίο η αλλαγή θερμοκρασίας δεν θα αποστέλλεται",
"auto_regulation_periode_min": "Διάρκεια σε λεπτά μεταξύ δύο ενημερώσεων ρύθμισης", "auto_regulation_periode_min": "Διάρκεια σε λεπτά μεταξύ δύο ενημερώσεων ρύθμισης",
"inverse_switch_command": "Για διακόπτη με πιλοτικό καλώδιο και δίοδο μπορεί να χρειαστεί να αντιστρέψετε την εντολή", "inverse_switch_command": "Για διακόπτη με πιλοτικό καλώδιο και δίοδο μπορεί να χρειαστεί να αντιστρέψετε την εντολή",
"auto_fan_mode": " Automatically activate fan when huge heating/cooling is necessary" "auto_fan_mode": "Automatically activate fan when huge heating/cooling is necessary"
} }
}, },
"tpi": { "tpi": {
@@ -216,7 +216,7 @@
"auto_regulation_dtemp": "Όριο ρύθμισης", "auto_regulation_dtemp": "Όριο ρύθμισης",
"auto_regulation_periode_min": "Ελάχιστη περίοδος ρύθμισης", "auto_regulation_periode_min": "Ελάχιστη περίοδος ρύθμισης",
"inverse_switch_command": "Αντίστροφη εντολή διακόπτη", "inverse_switch_command": "Αντίστροφη εντολή διακόπτη",
"auto_fan_mode": " Auto fan mode" "auto_fan_mode": "Auto fan mode"
}, },
"data_description": { "data_description": {
"heater_entity_id": "Υποχρεωτική ταυτότητα οντότητας θερμαντήρα", "heater_entity_id": "Υποχρεωτική ταυτότητα οντότητας θερμαντήρα",
@@ -237,7 +237,7 @@
"auto_regulation_dtemp": "Το κατώφλι σε °C κάτω από το οποίο η αλλαγή της θερμοκρασίας δεν θα αποστέλλεται", "auto_regulation_dtemp": "Το κατώφλι σε °C κάτω από το οποίο η αλλαγή της θερμοκρασίας δεν θα αποστέλλεται",
"auto_regulation_periode_min": "Διάρκεια σε λεπτά μεταξύ δύο ενημερώσεων ρύθμισης", "auto_regulation_periode_min": "Διάρκεια σε λεπτά μεταξύ δύο ενημερώσεων ρύθμισης",
"inverse_switch_command": "Για διακόπτες με πιλοτικό καλώδιο και δίοδο μπορεί να χρειαστεί να αντιστραφεί η εντολή", "inverse_switch_command": "Για διακόπτες με πιλοτικό καλώδιο και δίοδο μπορεί να χρειαστεί να αντιστραφεί η εντολή",
"auto_fan_mode": " Automatically activate fan when huge heating/cooling is necessary" "auto_fan_mode": "Automatically activate fan when huge heating/cooling is necessary"
} }
}, },
"tpi": { "tpi": {

View File

@@ -90,7 +90,7 @@
"auto_regulation_periode_min": "Regulation minimum period", "auto_regulation_periode_min": "Regulation minimum period",
"auto_regulation_use_device_temp": "Use internal temperature of the underlying", "auto_regulation_use_device_temp": "Use internal temperature of the underlying",
"inverse_switch_command": "Inverse switch command", "inverse_switch_command": "Inverse switch command",
"auto_fan_mode": " Auto fan mode" "auto_fan_mode": "Auto fan mode"
}, },
"data_description": { "data_description": {
"heater_entity_id": "Mandatory heater entity id", "heater_entity_id": "Mandatory heater entity id",
@@ -113,7 +113,7 @@
"auto_regulation_periode_min": "Duration in minutes between two regulation update", "auto_regulation_periode_min": "Duration in minutes between two regulation update",
"auto_regulation_use_device_temp": "Use the eventual internal temperature sensor of the underlying to speedup the self-regulation", "auto_regulation_use_device_temp": "Use the eventual internal temperature sensor of the underlying to speedup the self-regulation",
"inverse_switch_command": "For switch with pilot wire and diode you may need to inverse the command", "inverse_switch_command": "For switch with pilot wire and diode you may need to inverse the command",
"auto_fan_mode": " Automatically activate fan when huge heating/cooling is necessary" "auto_fan_mode": "Automatically activate fan when huge heating/cooling is necessary"
} }
}, },
"tpi": { "tpi": {
@@ -325,7 +325,7 @@
"auto_regulation_periode_min": "Regulation minimum period", "auto_regulation_periode_min": "Regulation minimum period",
"auto_regulation_use_device_temp": "Use internal temperature of the underlying", "auto_regulation_use_device_temp": "Use internal temperature of the underlying",
"inverse_switch_command": "Inverse switch command", "inverse_switch_command": "Inverse switch command",
"auto_fan_mode": " Auto fan mode" "auto_fan_mode": "Auto fan mode"
}, },
"data_description": { "data_description": {
"heater_entity_id": "Mandatory heater entity id", "heater_entity_id": "Mandatory heater entity id",
@@ -348,7 +348,7 @@
"auto_regulation_periode_min": "Duration in minutes between two regulation update", "auto_regulation_periode_min": "Duration in minutes between two regulation update",
"auto_regulation_use_device_temp": "Use the eventual internal temperature sensor of the underlying to speedup the self-regulation", "auto_regulation_use_device_temp": "Use the eventual internal temperature sensor of the underlying to speedup the self-regulation",
"inverse_switch_command": "For switch with pilot wire and diode you may need to invert the command", "inverse_switch_command": "For switch with pilot wire and diode you may need to invert the command",
"auto_fan_mode": " Automatically activate fan when huge heating/cooling is necessary" "auto_fan_mode": "Automatically activate fan when huge heating/cooling is necessary"
} }
}, },
"tpi": { "tpi": {

View File

@@ -337,7 +337,7 @@
"auto_regulation_periode_min": "Période minimale de régulation", "auto_regulation_periode_min": "Période minimale de régulation",
"auto_regulation_use_device_temp": "Utiliser la température interne du sous-jacent", "auto_regulation_use_device_temp": "Utiliser la température interne du sous-jacent",
"inverse_switch_command": "Inverser la commande", "inverse_switch_command": "Inverser la commande",
"auto_fan_mode": " Auto fan mode" "auto_fan_mode": "Auto fan mode"
}, },
"data_description": { "data_description": {
"heater_entity_id": "Entity id du 1er radiateur obligatoire", "heater_entity_id": "Entity id du 1er radiateur obligatoire",

View File

@@ -42,7 +42,7 @@
"valve_entity4_id": "Quarta valvola", "valve_entity4_id": "Quarta valvola",
"auto_regulation_mode": "Autoregolamentazione", "auto_regulation_mode": "Autoregolamentazione",
"inverse_switch_command": "Comando inverso", "inverse_switch_command": "Comando inverso",
"auto_fan_mode": " Auto fan mode" "auto_fan_mode": "Auto fan mode"
}, },
"data_description": { "data_description": {
"heater_entity_id": "Entity id obbligatoria del primo riscaldatore", "heater_entity_id": "Entity id obbligatoria del primo riscaldatore",
@@ -62,7 +62,7 @@
"valve_entity4_id": "Entity id della quarta valvola", "valve_entity4_id": "Entity id della quarta valvola",
"auto_regulation_mode": "Regolazione automatica della temperatura target", "auto_regulation_mode": "Regolazione automatica della temperatura target",
"inverse_switch_command": "Inverte il controllo dell'interruttore per un'installazione con filo pilota e diodo", "inverse_switch_command": "Inverte il controllo dell'interruttore per un'installazione con filo pilota e diodo",
"auto_fan_mode": " Automatically activate fan when huge heating/cooling is necessary" "auto_fan_mode": "Automatically activate fan when huge heating/cooling is necessary"
} }
}, },
"tpi": { "tpi": {
@@ -206,7 +206,7 @@
"valve_entity4_id": "Quarta valvola", "valve_entity4_id": "Quarta valvola",
"auto_regulation_mode": "Autoregolamentazione", "auto_regulation_mode": "Autoregolamentazione",
"inverse_switch_command": "Comando inverso", "inverse_switch_command": "Comando inverso",
"auto_fan_mode": " Auto fan mode" "auto_fan_mode": "Auto fan mode"
}, },
"data_description": { "data_description": {
"heater_entity_id": "Entity id obbligatoria del primo riscaldatore", "heater_entity_id": "Entity id obbligatoria del primo riscaldatore",
@@ -226,7 +226,7 @@
"valve_entity4_id": "Entity id della quarta valvola", "valve_entity4_id": "Entity id della quarta valvola",
"auto_regulation_mode": "Autoregolamentazione", "auto_regulation_mode": "Autoregolamentazione",
"inverse_switch_command": "Inverte il controllo dell'interruttore per un'installazione con filo pilota e diodo", "inverse_switch_command": "Inverte il controllo dell'interruttore per un'installazione con filo pilota e diodo",
"auto_fan_mode": " Automatically activate fan when huge heating/cooling is necessary" "auto_fan_mode": "Automatically activate fan when huge heating/cooling is necessary"
} }
}, },
"tpi": { "tpi": {

View File

@@ -612,12 +612,22 @@ class UnderlyingClimate(UnderlyingEntity):
if not self.is_initialized: if not self.is_initialized:
return return
data = { # Issue 508 we have to take care of service set_temperature or set_range
ATTR_ENTITY_ID: self._entity_id, target_temp = self.cap_sent_value(temperature)
"temperature": self.cap_sent_value(temperature), if (
"target_temp_high": max_temp, ClimateEntityFeature.TARGET_TEMPERATURE_RANGE
"target_temp_low": min_temp, in self._underlying_climate.supported_features
} ):
data = {
ATTR_ENTITY_ID: self._entity_id,
"target_temp_high": target_temp,
"target_temp_low": target_temp,
}
else:
data = {
ATTR_ENTITY_ID: self._entity_id,
"temperature": target_temp,
}
await self._hass.services.async_call( await self._hass.services.async_call(
CLIMATE_DOMAIN, CLIMATE_DOMAIN,

View File

@@ -3,5 +3,5 @@
"content_in_root": false, "content_in_root": false,
"render_readme": true, "render_readme": true,
"hide_default_branch": false, "hide_default_branch": false,
"homeassistant": "2024.6.1" "homeassistant": "2024.9.3"
} }

View File

@@ -1 +1 @@
homeassistant==2024.6.1 homeassistant==2024.9.3

View File

@@ -435,6 +435,86 @@ class MagicMockClimate(MagicMock):
return 19 return 19
class MagicMockClimateWithTemperatureRange(MagicMock):
"""A Magic Mock class for a underlying climate entity"""
@property
def temperature_unit(self): # pylint: disable=missing-function-docstring
return UnitOfTemperature.CELSIUS
@property
def hvac_mode(self): # pylint: disable=missing-function-docstring
return HVACMode.HEAT
@property
def hvac_action(self): # pylint: disable=missing-function-docstring
return HVACAction.IDLE
@property
def target_temperature(self): # pylint: disable=missing-function-docstring
return 15
@property
def current_temperature(self): # pylint: disable=missing-function-docstring
return 14
@property
def target_temperature_step( # pylint: disable=missing-function-docstring
self,
) -> float | None:
return 0.5
@property
def target_temperature_high( # pylint: disable=missing-function-docstring
self,
) -> float | None:
return 35
@property
def target_temperature_low( # pylint: disable=missing-function-docstring
self,
) -> float | None:
return 7
@property
def hvac_modes( # pylint: disable=missing-function-docstring
self,
) -> list[str] | None:
return [HVACMode.HEAT, HVACMode.OFF, HVACMode.COOL]
@property
def fan_modes( # pylint: disable=missing-function-docstring
self,
) -> list[str] | None:
return None
@property
def swing_modes( # pylint: disable=missing-function-docstring
self,
) -> list[str] | None:
return None
@property
def fan_mode(self) -> str | None: # pylint: disable=missing-function-docstring
return None
@property
def swing_mode(self) -> str | None: # pylint: disable=missing-function-docstring
return None
@property
def supported_features(self): # pylint: disable=missing-function-docstring
return ClimateEntityFeature.TARGET_TEMPERATURE_RANGE
@property
def min_temp(self): # pylint: disable=missing-function-docstring
return 10
@property
def max_temp(self): # pylint: disable=missing-function-docstring
return 31
class MockSwitch(SwitchEntity): class MockSwitch(SwitchEntity):
"""A fake switch to be used instead real switch""" """A fake switch to be used instead real switch"""

View File

@@ -657,8 +657,8 @@ async def test_bug_272(
{ {
"entity_id": "climate.mock_climate", "entity_id": "climate.mock_climate",
"temperature": 17.5, "temperature": 17.5,
"target_temp_high": 30, # "target_temp_high": 30,
"target_temp_low": 15, # "target_temp_low": 15,
}, },
), ),
] ]
@@ -687,8 +687,8 @@ async def test_bug_272(
{ {
"entity_id": "climate.mock_climate", "entity_id": "climate.mock_climate",
"temperature": 15, # the minimum acceptable "temperature": 15, # the minimum acceptable
"target_temp_high": 30, # "target_temp_high": 30,
"target_temp_low": 15, # "target_temp_low": 15,
}, },
), ),
] ]
@@ -714,8 +714,8 @@ async def test_bug_272(
{ {
"entity_id": "climate.mock_climate", "entity_id": "climate.mock_climate",
"temperature": 19, # the maximum acceptable "temperature": 19, # the maximum acceptable
"target_temp_high": 30, # "target_temp_high": 30,
"target_temp_low": 15, # "target_temp_low": 15,
}, },
), ),
] ]
@@ -924,3 +924,92 @@ async def test_bug_339(
assert api.nb_active_device_for_boiler == 1 assert api.nb_active_device_for_boiler == 1
entity.remove_thermostat() entity.remove_thermostat()
@pytest.mark.parametrize("expected_lingering_timers", [True])
async def test_bug_508(
hass: HomeAssistant,
skip_hass_states_is_state,
skip_turn_on_off_heater,
skip_send_event,
):
"""Test that it not possible to set the target temperature under the min_temp setting"""
tz = get_tz(hass) # pylint: disable=invalid-name
now: datetime = datetime.now(tz=tz)
entry = MockConfigEntry(
domain=DOMAIN,
title="TheOverClimateMockName",
unique_id="uniqueId",
# default value are min 15°, max 31°, step 0.1
data=PARTIAL_CLIMATE_CONFIG, # 5 minutes security delay
)
# Min_temp is 10 and max_temp is 31 and features contains TARGET_TEMPERATURE_RANGE
fake_underlying_climate = MagicMockClimateWithTemperatureRange()
with patch(
"custom_components.versatile_thermostat.base_thermostat.BaseThermostat.send_event"
), patch(
"custom_components.versatile_thermostat.underlyings.UnderlyingClimate.find_underlying_climate",
return_value=fake_underlying_climate,
), patch(
"homeassistant.core.ServiceRegistry.async_call"
) as mock_service_call:
entity = await create_thermostat(hass, entry, "climate.theoverclimatemockname")
assert entity
assert entity.name == "TheOverClimateMockName"
assert entity.is_over_climate is True
assert entity.hvac_mode is HVACMode.OFF
# The VTherm value and not the underlying value
assert entity.target_temperature_step == 0.1
assert entity.target_temperature == entity.min_temp
assert entity.is_regulated is True
assert mock_service_call.call_count == 0
# Set the hvac_mode to HEAT
await entity.async_set_hvac_mode(HVACMode.HEAT)
# Not In the accepted interval -> should be converted into 10 (the min) and send with target_temp_high and target_temp_low
await entity.async_set_temperature(temperature=8.5)
# MagicMock climate is already HEAT by default. So there is no SET_HAVC_MODE call
assert mock_service_call.call_count == 1
mock_service_call.assert_has_calls(
[
call.async_call(
"climate",
SERVICE_SET_TEMPERATURE,
{
"entity_id": "climate.mock_climate",
# "temperature": 17.5,
"target_temp_high": 10,
"target_temp_low": 10,
},
),
]
)
with patch("homeassistant.core.ServiceRegistry.async_call") as mock_service_call:
# Not In the accepted interval -> should be converted into 10 (the min) and send with target_temp_high and target_temp_low
await entity.async_set_temperature(temperature=32)
# MagicMock climate is already HEAT by default. So there is no SET_HAVC_MODE call
assert mock_service_call.call_count == 1
mock_service_call.assert_has_calls(
[
call.async_call(
"climate",
SERVICE_SET_TEMPERATURE,
{
"entity_id": "climate.mock_climate",
"target_temp_high": 31,
"target_temp_low": 31,
},
),
]
)

View File

@@ -254,6 +254,9 @@ async def test_over_switch_deactivate_preset(
CONF_HEATER_KEEP_ALIVE: 0, CONF_HEATER_KEEP_ALIVE: 0,
CONF_SECURITY_DELAY_MIN: 10, CONF_SECURITY_DELAY_MIN: 10,
CONF_MINIMAL_ACTIVATION_DELAY: 10, CONF_MINIMAL_ACTIVATION_DELAY: 10,
CONF_PROP_FUNCTION: PROPORTIONAL_FUNCTION_TPI,
CONF_TPI_COEF_INT: 0.6,
CONF_TPI_COEF_EXT: 0.01,
}, },
) )

View File

@@ -79,6 +79,7 @@ async def test_add_number_for_central_config(
CONF_SECURITY_MIN_ON_PERCENT: 0.5, CONF_SECURITY_MIN_ON_PERCENT: 0.5,
CONF_SECURITY_DEFAULT_ON_PERCENT: 0.2, CONF_SECURITY_DEFAULT_ON_PERCENT: 0.2,
CONF_USE_CENTRAL_BOILER_FEATURE: False, CONF_USE_CENTRAL_BOILER_FEATURE: False,
CONF_PROP_FUNCTION: PROPORTIONAL_FUNCTION_TPI,
} }
| temps, | temps,
) )
@@ -156,6 +157,7 @@ async def test_add_number_for_central_config_without_temp(
CONF_TEMP_MAX: 30, CONF_TEMP_MAX: 30,
CONF_TPI_COEF_INT: 0.5, CONF_TPI_COEF_INT: 0.5,
CONF_TPI_COEF_EXT: 0.02, CONF_TPI_COEF_EXT: 0.02,
CONF_PROP_FUNCTION: PROPORTIONAL_FUNCTION_TPI,
CONF_WINDOW_DELAY: 15, CONF_WINDOW_DELAY: 15,
CONF_WINDOW_AUTO_OPEN_THRESHOLD: 4, CONF_WINDOW_AUTO_OPEN_THRESHOLD: 4,
CONF_WINDOW_AUTO_CLOSE_THRESHOLD: 1, CONF_WINDOW_AUTO_CLOSE_THRESHOLD: 1,
@@ -250,6 +252,7 @@ async def test_add_number_for_central_config_without_temp_ac_mode(
CONF_AC_MODE: True, CONF_AC_MODE: True,
CONF_TPI_COEF_INT: 0.5, CONF_TPI_COEF_INT: 0.5,
CONF_TPI_COEF_EXT: 0.02, CONF_TPI_COEF_EXT: 0.02,
CONF_PROP_FUNCTION: PROPORTIONAL_FUNCTION_TPI,
CONF_WINDOW_DELAY: 15, CONF_WINDOW_DELAY: 15,
CONF_WINDOW_AUTO_OPEN_THRESHOLD: 4, CONF_WINDOW_AUTO_OPEN_THRESHOLD: 4,
CONF_WINDOW_AUTO_CLOSE_THRESHOLD: 1, CONF_WINDOW_AUTO_CLOSE_THRESHOLD: 1,
@@ -343,6 +346,7 @@ async def test_add_number_for_central_config_without_temp_restore(
CONF_AC_MODE: False, CONF_AC_MODE: False,
CONF_TPI_COEF_INT: 0.5, CONF_TPI_COEF_INT: 0.5,
CONF_TPI_COEF_EXT: 0.02, CONF_TPI_COEF_EXT: 0.02,
CONF_PROP_FUNCTION: PROPORTIONAL_FUNCTION_TPI,
CONF_WINDOW_DELAY: 15, CONF_WINDOW_DELAY: 15,
CONF_WINDOW_AUTO_OPEN_THRESHOLD: 4, CONF_WINDOW_AUTO_OPEN_THRESHOLD: 4,
CONF_WINDOW_AUTO_CLOSE_THRESHOLD: 1, CONF_WINDOW_AUTO_CLOSE_THRESHOLD: 1,
@@ -441,6 +445,7 @@ async def test_add_number_for_over_switch_use_central(
CONF_AC_MODE: False, CONF_AC_MODE: False,
CONF_TPI_COEF_INT: 0.5, CONF_TPI_COEF_INT: 0.5,
CONF_TPI_COEF_EXT: 0.02, CONF_TPI_COEF_EXT: 0.02,
CONF_PROP_FUNCTION: PROPORTIONAL_FUNCTION_TPI,
CONF_USE_PRESENCE_CENTRAL_CONFIG: True, CONF_USE_PRESENCE_CENTRAL_CONFIG: True,
CONF_USE_ADVANCED_CENTRAL_CONFIG: True, CONF_USE_ADVANCED_CENTRAL_CONFIG: True,
CONF_USE_MAIN_CENTRAL_CONFIG: True, CONF_USE_MAIN_CENTRAL_CONFIG: True,
@@ -666,6 +671,7 @@ async def test_add_number_for_over_switch_use_central_presets_and_restore(
CONF_AC_MODE: False, CONF_AC_MODE: False,
CONF_TPI_COEF_INT: 0.5, CONF_TPI_COEF_INT: 0.5,
CONF_TPI_COEF_EXT: 0.02, CONF_TPI_COEF_EXT: 0.02,
CONF_PROP_FUNCTION: PROPORTIONAL_FUNCTION_TPI,
CONF_CYCLE_MIN: 5, CONF_CYCLE_MIN: 5,
CONF_HEATER: "switch.mock_switch1", CONF_HEATER: "switch.mock_switch1",
CONF_USE_PRESENCE_FEATURE: True, CONF_USE_PRESENCE_FEATURE: True,
@@ -788,6 +794,7 @@ async def test_change_central_config_temperature(
CONF_TEMP_MAX: 30, CONF_TEMP_MAX: 30,
CONF_TPI_COEF_INT: 0.5, CONF_TPI_COEF_INT: 0.5,
CONF_TPI_COEF_EXT: 0.02, CONF_TPI_COEF_EXT: 0.02,
CONF_PROP_FUNCTION: PROPORTIONAL_FUNCTION_TPI,
CONF_CYCLE_MIN: 5, CONF_CYCLE_MIN: 5,
CONF_VALVE: "switch.mock_valve", CONF_VALVE: "switch.mock_valve",
CONF_USE_PRESENCE_FEATURE: True, CONF_USE_PRESENCE_FEATURE: True,
@@ -823,6 +830,7 @@ async def test_change_central_config_temperature(
CONF_TPI_COEF_INT: 0.5, CONF_TPI_COEF_INT: 0.5,
CONF_TPI_COEF_EXT: 0.02, CONF_TPI_COEF_EXT: 0.02,
CONF_CYCLE_MIN: 5, CONF_CYCLE_MIN: 5,
CONF_PROP_FUNCTION: PROPORTIONAL_FUNCTION_TPI,
CONF_VALVE: "switch.mock_valve", CONF_VALVE: "switch.mock_valve",
CONF_USE_PRESENCE_FEATURE: True, CONF_USE_PRESENCE_FEATURE: True,
CONF_USE_PRESENCE_CENTRAL_CONFIG: False, CONF_USE_PRESENCE_CENTRAL_CONFIG: False,
@@ -905,6 +913,7 @@ async def test_change_vtherm_temperature(
CONF_TPI_COEF_INT: 0.5, CONF_TPI_COEF_INT: 0.5,
CONF_TPI_COEF_EXT: 0.02, CONF_TPI_COEF_EXT: 0.02,
CONF_CYCLE_MIN: 5, CONF_CYCLE_MIN: 5,
CONF_PROP_FUNCTION: PROPORTIONAL_FUNCTION_TPI,
CONF_VALVE: "switch.mock_valve", CONF_VALVE: "switch.mock_valve",
CONF_USE_PRESENCE_FEATURE: True, CONF_USE_PRESENCE_FEATURE: True,
CONF_USE_PRESENCE_CENTRAL_CONFIG: True, CONF_USE_PRESENCE_CENTRAL_CONFIG: True,
@@ -939,6 +948,7 @@ async def test_change_vtherm_temperature(
CONF_TPI_COEF_INT: 0.5, CONF_TPI_COEF_INT: 0.5,
CONF_TPI_COEF_EXT: 0.02, CONF_TPI_COEF_EXT: 0.02,
CONF_CYCLE_MIN: 5, CONF_CYCLE_MIN: 5,
CONF_PROP_FUNCTION: PROPORTIONAL_FUNCTION_TPI,
CONF_VALVE: "switch.mock_valve", CONF_VALVE: "switch.mock_valve",
CONF_USE_PRESENCE_FEATURE: True, CONF_USE_PRESENCE_FEATURE: True,
CONF_USE_PRESENCE_CENTRAL_CONFIG: False, CONF_USE_PRESENCE_CENTRAL_CONFIG: False,
@@ -1022,6 +1032,7 @@ async def test_change_vtherm_temperature_with_presence(
CONF_TEMP_MAX: 30, CONF_TEMP_MAX: 30,
CONF_TPI_COEF_INT: 0.5, CONF_TPI_COEF_INT: 0.5,
CONF_TPI_COEF_EXT: 0.02, CONF_TPI_COEF_EXT: 0.02,
CONF_PROP_FUNCTION: PROPORTIONAL_FUNCTION_TPI,
CONF_CYCLE_MIN: 5, CONF_CYCLE_MIN: 5,
CONF_AC_MODE: True, CONF_AC_MODE: True,
CONF_VALVE: "switch.mock_valve", CONF_VALVE: "switch.mock_valve",
@@ -1063,6 +1074,7 @@ async def test_change_vtherm_temperature_with_presence(
CONF_TEMP_MAX: 30, CONF_TEMP_MAX: 30,
CONF_TPI_COEF_INT: 0.5, CONF_TPI_COEF_INT: 0.5,
CONF_TPI_COEF_EXT: 0.02, CONF_TPI_COEF_EXT: 0.02,
CONF_PROP_FUNCTION: PROPORTIONAL_FUNCTION_TPI,
CONF_CYCLE_MIN: 5, CONF_CYCLE_MIN: 5,
CONF_VALVE: "switch.mock_valve", CONF_VALVE: "switch.mock_valve",
CONF_USE_PRESENCE_FEATURE: True, CONF_USE_PRESENCE_FEATURE: True,

View File

@@ -3,7 +3,10 @@
from homeassistant.components.climate import HVACMode from homeassistant.components.climate import HVACMode
from custom_components.versatile_thermostat.base_thermostat import BaseThermostat from custom_components.versatile_thermostat.base_thermostat import BaseThermostat
from custom_components.versatile_thermostat.prop_algorithm import PropAlgorithm from custom_components.versatile_thermostat.prop_algorithm import (
PropAlgorithm,
PROPORTIONAL_FUNCTION_TPI,
)
from .commons import * # pylint: disable=wildcard-import, unused-wildcard-import from .commons import * # pylint: disable=wildcard-import, unused-wildcard-import
@@ -121,3 +124,123 @@ async def test_tpi_calculation(
assert tpi_algo.calculated_on_percent == 0 assert tpi_algo.calculated_on_percent == 0
assert tpi_algo.on_time_sec == 0 assert tpi_algo.on_time_sec == 0
assert tpi_algo.off_time_sec == 300 assert tpi_algo.off_time_sec == 300
@pytest.mark.parametrize("expected_lingering_tasks", [True])
@pytest.mark.parametrize("expected_lingering_timers", [True])
async def test_wrong_tpi_parameters(
hass: HomeAssistant, skip_hass_states_is_state: None
): # pylint: disable=unused-argument
"""Test the wrong TPI parameters"""
# Nominal case
try:
algo = PropAlgorithm(
PROPORTIONAL_FUNCTION_TPI,
0.6,
0.01,
5,
1,
"entity_id",
)
# We should not be there
assert True
except TypeError as e:
# the normal case
assert False
# Test TPI function
try:
algo = PropAlgorithm(
"WRONG",
1,
0,
2,
3,
"entity_id",
)
# We should not be there
assert False
except TypeError as e:
# the normal case
pass
# Test coef_int
try:
algo = PropAlgorithm(
PROPORTIONAL_FUNCTION_TPI,
None,
0,
2,
3,
"entity_id",
)
# We should not be there
assert False
except TypeError as e:
# the normal case
pass
# Test coef_ext
try:
algo = PropAlgorithm(
PROPORTIONAL_FUNCTION_TPI,
0.6,
None,
2,
3,
"entity_id",
)
# We should not be there
assert False
except TypeError as e:
# the normal case
pass
# Test cycle_min
try:
algo = PropAlgorithm(
PROPORTIONAL_FUNCTION_TPI,
0.6,
0.00001,
None,
3,
"entity_id",
)
# We should not be there
assert False
except TypeError as e:
# the normal case
pass
# Test minimal_activation_delay
try:
algo = PropAlgorithm(
PROPORTIONAL_FUNCTION_TPI,
0.6,
0.00001,
0,
None,
"entity_id",
)
# We should not be there
assert False
except TypeError as e:
# the normal case
pass
# Test vtherm_entity_id
try:
algo = PropAlgorithm(
PROPORTIONAL_FUNCTION_TPI,
0.6,
0.00001,
0,
12,
None,
)
# We should not be there
assert False
except TypeError as e:
# the normal case
pass