Skip to content

Commit

Permalink
Feature #722 minimum valve opening (#731)
Browse files Browse the repository at this point in the history
* ok + testu ok

* Feature #722 - Add a minimum opening degree for valve regulation

* Feature #722 - Add a minimum opening degree for valve regulation

---------

Co-authored-by: Jean-Marc Collin <[email protected]>
  • Loading branch information
jmcollin78 and Jean-Marc Collin authored Dec 21, 2024
1 parent 24f6445 commit d9791f6
Show file tree
Hide file tree
Showing 16 changed files with 270 additions and 16 deletions.
17 changes: 17 additions & 0 deletions custom_components/versatile_thermostat/config_flow.py
Original file line number Diff line number Diff line change
Expand Up @@ -259,6 +259,21 @@ async def validate_input(self, data: dict, step_id) -> None:
if not self.check_valve_regulation_nb_entities(data, step_id):
raise ValveRegulationNbEntitiesIncorrect()

# Check that the min_opening_degrees is correctly set
raw_list = data.get(CONF_MIN_OPENING_DEGREES, None)
if raw_list:
try:
# Validation : Convertir la liste saisie
int_list = [int(x.strip()) for x in raw_list.split(",")]

# Optionnel : Vérifiez des conditions supplémentaires sur la liste
if any(x < 0 for x in int_list):
raise ValueError
except ValueError as exc:
raise ValveRegulationMinOpeningDegreesIncorrect(
CONF_MIN_OPENING_DEGREES
) from exc

def check_config_complete(self, infos) -> bool:
"""True if the config is now complete (ie all mandatory attributes are set)"""
is_central_config = (
Expand Down Expand Up @@ -399,6 +414,8 @@ async def generic_step(self, step_id, data_schema, user_input, next_step_functio
errors["base"] = "configuration_not_complete"
except ValveRegulationNbEntitiesIncorrect as err:
errors["base"] = "valve_regulation_nb_entities_incorrect"
except ValveRegulationMinOpeningDegreesIncorrect as err:
errors[str(err)] = "min_opening_degrees_format"
except Exception: # pylint: disable=broad-except
_LOGGER.exception("Unexpected exception")
errors["base"] = "unknown"
Expand Down
1 change: 1 addition & 0 deletions custom_components/versatile_thermostat/config_schema.py
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,7 @@
PROPORTIONAL_FUNCTION_TPI,
]
),
vol.Optional(CONF_MIN_OPENING_DEGREES, default=""): str,
}
)

Expand Down
5 changes: 5 additions & 0 deletions custom_components/versatile_thermostat/const.py
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,7 @@
CONF_OFFSET_CALIBRATION_LIST = "offset_calibration_entity_ids"
CONF_OPENING_DEGREE_LIST = "opening_degree_entity_ids"
CONF_CLOSING_DEGREE_LIST = "closing_degree_entity_ids"
CONF_MIN_OPENING_DEGREES = "min_opening_degrees"

# Deprecated
CONF_HEATER = "heater_entity_id"
Expand Down Expand Up @@ -552,6 +553,10 @@ class ValveRegulationNbEntitiesIncorrect(HomeAssistantError):
The number of specific entities is incorrect."""


class ValveRegulationMinOpeningDegreesIncorrect(HomeAssistantError):
"""Error to indicate that the minimal opening degrees is not a list of int separated by coma"""


class overrides: # pylint: disable=invalid-name
"""An annotation to inform overrides"""

Expand Down
2 changes: 1 addition & 1 deletion custom_components/versatile_thermostat/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,6 @@
"quality_scale": "silver",
"requirements": [],
"ssdp": [],
"version": "6.8.3",
"version": "6.8.4",
"zeroconf": []
}
15 changes: 10 additions & 5 deletions custom_components/versatile_thermostat/strings.json
Original file line number Diff line number Diff line change
Expand Up @@ -224,13 +224,15 @@
"offset_calibration_entity_ids": "Offset calibration entities",
"opening_degree_entity_ids": "Opening degree entities",
"closing_degree_entity_ids": "Closing degree entities",
"proportional_function": "Algorithm"
"proportional_function": "Algorithm",
"min_opening_degrees": "Min opening degrees"
},
"data_description": {
"offset_calibration_entity_ids": "The list of the 'offset calibration' entities. Set it if your TRV have the entity for better regulation. There should be one per underlying climate entities",
"opening_degree_entity_ids": "The list of the 'opening degree' entities. There should be one per underlying climate entities",
"closing_degree_entity_ids": "The list of the 'closing degree' entities. Set it if your TRV have the entity for better regulation. There should be one per underlying climate entities",
"proportional_function": "Algorithm to use (TPI is the only one for now)"
"proportional_function": "Algorithm to use (TPI is the only one for now)",
"min_opening_degrees": "A comma seperated list of minimal opening degrees. Default to 0. Example: 20, 25, 30"
}
}
},
Expand Down Expand Up @@ -468,13 +470,15 @@
"offset_calibration_entity_ids": "Offset calibration entities",
"opening_degree_entity_ids": "Opening degree entities",
"closing_degree_entity_ids": "Closing degree entities",
"proportional_function": "Algorithm"
"proportional_function": "Algorithm",
"min_opening_degrees": "Min opening degrees"
},
"data_description": {
"offset_calibration_entity_ids": "The list of the 'offset calibration' entities. Set it if your TRV have the entity for better regulation. There should be one per underlying climate entities",
"opening_degree_entity_ids": "The list of the 'opening degree' entities. There should be one per underlying climate entities",
"closing_degree_entity_ids": "The list of the 'closing degree' entities. Set it if your TRV have the entity for better regulation. There should be one per underlying climate entities",
"proportional_function": "Algorithm to use (TPI is the only one for now)"
"proportional_function": "Algorithm to use (TPI is the only one for now)",
"min_opening_degrees": "A comma seperated list of minimal opening degrees. Default to 0. Example: 20, 25, 30"
}
}
},
Expand All @@ -484,7 +488,8 @@
"window_open_detection_method": "Only one window open detection method should be used. Use either window sensor or automatic detection through temperature threshold but not both",
"no_central_config": "You cannot check 'use central configuration' because no central configuration was found. You need to create a Versatile Thermostat of type 'Central Configuration' to use it.",
"service_configuration_format": "The format of the service configuration is wrong",
"valve_regulation_nb_entities_incorrect": "The number of valve entities for valve regulation should be equal to the number of underlyings"
"valve_regulation_nb_entities_incorrect": "The number of valve entities for valve regulation should be equal to the number of underlyings",
"min_opening_degrees_format": "A comma separated list of positive integer is expected. Example: 20, 25, 30"
},
"abort": {
"already_configured": "Device is already configured"
Expand Down
19 changes: 19 additions & 0 deletions custom_components/versatile_thermostat/thermostat_climate_valve.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ class ThermostatOverClimateValve(ThermostatOverClimate):
"tpi_coef_int",
"tpi_coef_ext",
"power_percent",
"min_opening_degrees",
}
)
)
Expand All @@ -51,6 +52,7 @@ def __init__(
self._last_calculation_timestamp: datetime | None = None
self._auto_regulation_dpercent: float | None = None
self._auto_regulation_period_min: int | None = None
self._min_opening_degress: list[int] = []

super().__init__(hass, unique_id, name, entry_infos)

Expand Down Expand Up @@ -86,6 +88,14 @@ def post_init(self, config_entry: ConfigData):
offset_list = config_entry.get(CONF_OFFSET_CALIBRATION_LIST, [])
opening_list = config_entry.get(CONF_OPENING_DEGREE_LIST)
closing_list = config_entry.get(CONF_CLOSING_DEGREE_LIST, [])

self._min_opening_degrees = config_entry.get(CONF_MIN_OPENING_DEGREES, None)
min_opening_degrees_list = []
if self._min_opening_degrees:
min_opening_degrees_list = [
int(x.strip()) for x in self._min_opening_degrees.split(",")
]

for idx, _ in enumerate(config_entry.get(CONF_UNDERLYING_LIST)):
offset = offset_list[idx] if idx < len(offset_list) else None
# number of opening should equal number of underlying
Expand All @@ -98,6 +108,11 @@ def post_init(self, config_entry: ConfigData):
opening_degree_entity_id=opening,
closing_degree_entity_id=closing,
climate_underlying=self._underlyings[idx],
min_opening_degree=(
min_opening_degrees_list[idx]
if idx < len(min_opening_degrees_list)
else 0
),
)
self._underlyings_valve_regulation.append(under)

Expand Down Expand Up @@ -130,6 +145,10 @@ def update_custom_attributes(self):
self._attr_extra_state_attributes["tpi_coef_int"] = self._tpi_coef_int
self._attr_extra_state_attributes["tpi_coef_ext"] = self._tpi_coef_ext

self._attr_extra_state_attributes["min_opening_degrees"] = (
self._min_opening_degrees
)

self._attr_extra_state_attributes["valve_open_percent"] = (
self.valve_open_percent
)
Expand Down
12 changes: 8 additions & 4 deletions custom_components/versatile_thermostat/translations/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -224,13 +224,15 @@
"offset_calibration_entity_ids": "Offset calibration entities",
"opening_degree_entity_ids": "Opening degree entities",
"closing_degree_entity_ids": "Closing degree entities",
"proportional_function": "Algorithm"
"proportional_function": "Algorithm",
"min_opening_degrees": "Min opening degrees"
},
"data_description": {
"offset_calibration_entity_ids": "The list of the 'offset calibration' entities. Set it if your TRV have the entity for better regulation. There should be one per underlying climate entities",
"opening_degree_entity_ids": "The list of the 'opening degree' entities. There should be one per underlying climate entities",
"closing_degree_entity_ids": "The list of the 'closing degree' entities. Set it if your TRV have the entity for better regulation. There should be one per underlying climate entities",
"proportional_function": "Algorithm to use (TPI is the only one for now)"
"proportional_function": "Algorithm to use (TPI is the only one for now)",
"min_opening_degrees": "A comma seperated list of minimal opening degrees. Default to 0. Example: 20, 25, 30"
}
}
},
Expand Down Expand Up @@ -468,13 +470,15 @@
"offset_calibration_entity_ids": "Offset calibration entities",
"opening_degree_entity_ids": "Opening degree entities",
"closing_degree_entity_ids": "Closing degree entities",
"proportional_function": "Algorithm"
"proportional_function": "Algorithm",
"min_opening_degrees": "Min opening degrees"
},
"data_description": {
"offset_calibration_entity_ids": "The list of the 'offset calibration' entities. Set it if your TRV have the entity for better regulation. There should be one per underlying climate entities",
"opening_degree_entity_ids": "The list of the 'opening degree' entities. There should be one per underlying climate entities",
"closing_degree_entity_ids": "The list of the 'closing degree' entities. Set it if your TRV have the entity for better regulation. There should be one per underlying climate entities",
"proportional_function": "Algorithm to use (TPI is the only one for now)"
"proportional_function": "Algorithm to use (TPI is the only one for now)",
"min_opening_degrees": "A comma seperated list of minimal opening degrees. Default to 0. Example: 20, 25, 30"
}
}
},
Expand Down
15 changes: 10 additions & 5 deletions custom_components/versatile_thermostat/translations/fr.json
Original file line number Diff line number Diff line change
Expand Up @@ -224,13 +224,15 @@
"offset_calibration_entity_ids": "Entités de 'calibrage du décalage''",
"opening_degree_entity_ids": "Entités 'ouverture de vanne'",
"closing_degree_entity_ids": "Entités 'fermeture de la vanne'",
"proportional_function": "Algorithme"
"proportional_function": "Algorithme",
"min_opening_degrees": "Ouvertures minimales"
},
"data_description": {
"offset_calibration_entity_ids": "La liste des entités 'calibrage du décalage' (offset calibration). Configurez le si votre TRV possède cette fonction pour une meilleure régulation. Il doit y en avoir une par entité climate sous-jacente",
"opening_degree_entity_ids": "La liste des entités 'ouverture de vanne'. Il doit y en avoir une par entité climate sous-jacente",
"closing_degree_entity_ids": "La liste des entités 'fermeture de la vanne'. Configurez le si votre TRV possède cette fonction pour une meilleure régulation. Il doit y en avoir une par entité climate sous-jacente",
"proportional_function": "Algorithme à utiliser (seulement TPI est disponible)"
"proportional_function": "Algorithme à utiliser (seulement TPI est disponible)",
"min_opening_degrees": "Une liste séparée par des virgules de minimum d'ouverture. Valeur par défaut : 0. Exemple : 20, 25, 30"
}
}
},
Expand Down Expand Up @@ -462,13 +464,15 @@
"offset_calibration_entity_ids": "Entités de 'calibrage du décalage''",
"opening_degree_entity_ids": "Entités 'ouverture de vanne'",
"closing_degree_entity_ids": "Entités 'fermeture de la vanne'",
"proportional_function": "Algorithme"
"proportional_function": "Algorithme",
"min_opening_degrees": "Ouvertures minimales"
},
"data_description": {
"offset_calibration_entity_ids": "La liste des entités 'calibrage du décalage' (offset calibration). Configurez le si votre TRV possède cette fonction pour une meilleure régulation. Il doit y en avoir une par entité climate sous-jacente",
"opening_degree_entity_ids": "La liste des entités 'ouverture de vanne'. Il doit y en avoir une par entité climate sous-jacente",
"closing_degree_entity_ids": "La liste des entités 'fermeture de la vanne'. Configurez le si votre TRV possède cette fonction pour une meilleure régulation. Il doit y en avoir une par entité climate sous-jacente",
"proportional_function": "Algorithme à utiliser (seulement TPI est disponible)"
"proportional_function": "Algorithme à utiliser (seulement TPI est disponible)",
"min_opening_degrees": "Une liste séparée par des virgules de minimum d'ouverture. Valeur par défaut : 0. Exemple : 20, 25, 30"
}
}
},
Expand All @@ -478,7 +482,8 @@
"window_open_detection_method": "Une seule méthode de détection des ouvertures ouvertes doit être utilisée. Utilisez le détecteur d'ouverture ou les seuils de température mais pas les deux.",
"no_central_config": "Vous ne pouvez pas cocher 'Utiliser la configuration centrale' car aucune configuration centrale n'a été trouvée. Vous devez créer un Versatile Thermostat de type 'Central Configuration' pour pouvoir l'utiliser.",
"service_configuration_format": "Mauvais format de la configuration du service",
"valve_regulation_nb_entities_incorrect": "Le nombre d'entités pour la régulation par vanne doit être égal au nombre d'entité sous-jacentes"
"valve_regulation_nb_entities_incorrect": "Le nombre d'entités pour la régulation par vanne doit être égal au nombre d'entité sous-jacentes",
"min_opening_degrees_format": "Une liste d'entiers positifs séparés par des ',' est attendu. Exemple : 20, 25, 30"
},
"abort": {
"already_configured": "Le device est déjà configuré"
Expand Down
10 changes: 10 additions & 0 deletions custom_components/versatile_thermostat/underlyings.py
Original file line number Diff line number Diff line change
Expand Up @@ -1029,6 +1029,7 @@ def __init__(
opening_degree_entity_id: str,
closing_degree_entity_id: str,
climate_underlying: UnderlyingClimate,
min_opening_degree: int = 0,
) -> None:
"""Initialize the underlying TRV with valve regulation"""
super().__init__(
Expand All @@ -1045,6 +1046,7 @@ def __init__(
self._max_opening_degree: float = None
self._min_offset_calibration: float = None
self._max_offset_calibration: float = None
self._min_opening_degree: int = min_opening_degree

async def send_percent_open(self):
"""Send the percent open to the underlying valve"""
Expand Down Expand Up @@ -1079,6 +1081,9 @@ async def send_percent_open(self):
return

# Send opening_degree
if 0 < self._percent_open < self._min_opening_degree:
self._percent_open = self._min_opening_degree

await super().send_percent_open()

# Send closing_degree if set
Expand Down Expand Up @@ -1138,6 +1143,11 @@ def closing_degree_entity_id(self) -> str:
"""The offset_calibration_entity_id"""
return self._closing_degree_entity_id

@property
def min_opening_degree(self) -> int:
"""The minimum opening degree"""
return self._min_opening_degree

@property
def have_closing_degree_entity(self) -> bool:
"""Return True if the underlying have a closing_degree entity"""
Expand Down
Binary file added documentation/en/images/1.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified documentation/en/images/config-self-regulation-valve-2.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions documentation/en/self-regulation.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ You need to provide:
1. As many valve opening control entities as there are underlying devices, and in the same order. These parameters are mandatory.
2. As many temperature calibration entities as there are underlying devices, and in the same order. These parameters are optional; they must either all be provided or none.
3. As many valve closure control entities as there are underlying devices, and in the same order. These parameters are optional; they must either all be provided or none.
4. A list of minimum opening values ​​for the valve when it needs to be opened. This field is a list of integers. If the valve needs to be opened, it will be opened at a minimum of this opening value. This allows enough water to pass through when it needs to be opened.

The opening rate calculation algorithm is based on the _TPI_ algorithm described [here](algorithms.md). This is the same algorithm used for _VTherms_ `over_switch` and `over_valve`.

Expand Down
Binary file modified documentation/fr/images/config-self-regulation-valve-2.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 2 additions & 1 deletion documentation/fr/self-regulation.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,8 @@ Elle permet de configurer les entités de contrôle de la vanne :
Vous devez donner :
1. autant d'entités de contrôle d'ouverture de la vanne qu'il y a de sous-jacents et dans le même odre. Ces paramètres sont obligatoires,
2. autant d'entités de calibrage du décalage de température qu'il y a de sous-jacents et dans le même ordre. Ces paramètres sont facultatifs ; ils doivent être tous founis ou aucun,
3. autant d'entités de de contrôile du taux de fermture qu'il y a de sous-jacents et dans le même ordre. Ces paramètres sont facultatifs ; ils doivent être tous founis ou aucun
3. autant d'entités de de contrôile du taux de fermture qu'il y a de sous-jacents et dans le même ordre. Ces paramètres sont facultatifs ; ils doivent être tous founis ou aucun,
4. une liste de valeurs minimales d'ouverture de la vanne lorsqu'elle doit être ouverte. Ce champ est une liste d'entier. Si la vanne doit être ouverte, elle le sera au minimum avec cette valeur d'ouverture. Cela permet de laisser passer suffisamment d'eau lorsqu'elle doit être ouverte.

L'algorithme de calcul du taux d'ouverture est basé sur le _TPI_ qui est décrit [ici](algorithms.md). C'est le même algorithme qui est utilisé pour les _VTherm_ `over_switch` et `over_valve`.

Expand Down
3 changes: 3 additions & 0 deletions tests/test_config_flow.py
Original file line number Diff line number Diff line change
Expand Up @@ -1581,6 +1581,7 @@ async def test_user_config_flow_over_climate_valve(
CONF_OFFSET_CALIBRATION_LIST: ["number.offset_calibration1"],
CONF_OPENING_DEGREE_LIST: ["number.opening_degree1"],
CONF_CLOSING_DEGREE_LIST: ["number.closing_degree1"],
CONF_MIN_OPENING_DEGREES: "10, 20,0",
},
)
assert result["type"] == FlowResultType.FORM
Expand Down Expand Up @@ -1619,6 +1620,7 @@ async def test_user_config_flow_over_climate_valve(
"number.opening_degree2",
],
CONF_CLOSING_DEGREE_LIST: [],
CONF_MIN_OPENING_DEGREES: "10, 20,0",
},
)
assert result["type"] == FlowResultType.MENU
Expand Down Expand Up @@ -1715,6 +1717,7 @@ async def test_user_config_flow_over_climate_valve(
CONF_PROP_FUNCTION: PROPORTIONAL_FUNCTION_TPI,
CONF_TPI_COEF_INT: 0.3,
CONF_TPI_COEF_EXT: 0.1,
CONF_MIN_OPENING_DEGREES: "10, 20,0",
}
assert result["result"]
assert result["result"].domain == DOMAIN
Expand Down
Loading

0 comments on commit d9791f6

Please sign in to comment.