Feature 194 - add auto-regulation Export mode (#197)

* Add config, read and create algo
* Tests ok

---------

Co-authored-by: Jean-Marc Collin <jean-marc.collin-extern@renault.com>
This commit is contained in:
Jean-Marc Collin
2023-11-17 07:40:07 +01:00
committed by GitHub
parent 72ede4a03f
commit 1375b3c53a
13 changed files with 358 additions and 52 deletions

View File

@@ -4,16 +4,84 @@ from __future__ import annotations
from typing import Dict
import logging
import voluptuous as vol
from homeassistant.config_entries import ConfigEntry
from homeassistant.config_entries import ConfigEntry, ConfigType
from homeassistant.core import HomeAssistant
from .base_thermostat import BaseThermostat
from .const import DOMAIN, PLATFORMS
from .const import (
DOMAIN,
PLATFORMS,
CONF_AUTO_REGULATION_LIGHT,
CONF_AUTO_REGULATION_MEDIUM,
CONF_AUTO_REGULATION_STRONG,
CONF_AUTO_REGULATION_SLOW,
CONF_AUTO_REGULATION_EXPERT,
)
from .vtherm_api import VersatileThermostatAPI
_LOGGER = logging.getLogger(__name__)
SELF_REGULATION_PARAM_SCHEMA = (
vol.Schema(
{
vol.Required("kp"): vol.Coerce(float),
vol.Required("ki"): vol.Coerce(float),
vol.Required("k_ext"): vol.Coerce(float),
vol.Required("offset_max"): vol.Coerce(float),
vol.Required("stabilization_threshold"): vol.Coerce(float),
vol.Required("accumulated_error_threshold"): vol.Coerce(float),
}
),
)
CONFIG_SCHEMA = vol.Schema(
{
DOMAIN: vol.Schema(
{
CONF_AUTO_REGULATION_EXPERT: vol.Schema(
{
vol.Required("kp"): vol.Coerce(float),
vol.Required("ki"): vol.Coerce(float),
vol.Required("k_ext"): vol.Coerce(float),
vol.Required("offset_max"): vol.Coerce(float),
vol.Required("stabilization_threshold"): vol.Coerce(float),
vol.Required("accumulated_error_threshold"): vol.Coerce(float),
}
),
}
),
},
extra=vol.ALLOW_EXTRA,
)
async def async_setup(
hass: HomeAssistant, config: ConfigType
): # pylint: disable=unused-argument
"""Initialisation de l'intégration"""
_LOGGER.info(
"Initializing %s integration with config: %s",
DOMAIN,
config.get(DOMAIN),
)
hass.data.setdefault(DOMAIN, {})
# L'argument config contient votre fichier configuration.yaml
vtherm_config = config.get(DOMAIN)
if vtherm_config is not None:
api: VersatileThermostatAPI = VersatileThermostatAPI.get_vtherm_api(hass)
api.set_global_config(vtherm_config)
else:
_LOGGER.info("No global config from configuration.yaml available")
return True
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
"""Set up Versatile Thermostat from a config entry."""
@@ -24,11 +92,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
entry.data,
)
# hass.data.setdefault(DOMAIN, {})
api: VersatileThermostatAPI = hass.data.get(DOMAIN)
if api is None:
api = VersatileThermostatAPI(hass)
api: VersatileThermostatAPI = VersatileThermostatAPI.get_vtherm_api(hass)
api.add_entry(entry)
@@ -46,7 +110,7 @@ async def update_listener(hass: HomeAssistant, entry: ConfigEntry) -> None:
async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
"""Unload a config entry."""
api: VersatileThermostatAPI = hass.data.get(DOMAIN)
api: VersatileThermostatAPI = VersatileThermostatAPI.get_vtherm_api(hass)
if unload_ok := await hass.config_entries.async_unload_platforms(entry, PLATFORMS):
if api:
@@ -55,43 +119,6 @@ async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
return unload_ok
class VersatileThermostatAPI(dict):
"""The VersatileThermostatAPI"""
_hass: HomeAssistant
# _entries: Dict(str, ConfigEntry)
def __init__(self, hass: HomeAssistant) -> None:
_LOGGER.debug("building a VersatileThermostatAPI")
super().__init__()
self._hass = hass
# self._entries = dict()
# Add the API in hass.data
self._hass.data[DOMAIN] = self
def add_entry(self, entry: ConfigEntry):
"""Add a new entry"""
_LOGGER.debug("Add the entry %s", entry.entry_id)
# self._entries[entry.entry_id] = entry
# Add the entry in hass.data
self._hass.data[DOMAIN][entry.entry_id] = entry
def remove_entry(self, entry: ConfigEntry):
"""Remove an entry"""
_LOGGER.debug("Remove the entry %s", entry.entry_id)
# self._entries.pop(entry.entry_id)
self._hass.data[DOMAIN].pop(entry.entry_id)
# If not more entries are preset, remove the API
if len(self) == 0:
_LOGGER.debug("No more entries-> Remove the API from DOMAIN")
self._hass.data.pop(DOMAIN)
@property
def hass(self):
"""Get the HomeAssistant object"""
return self._hass
# Example migration function
async def async_migrate_entry(hass: HomeAssistant, config_entry: ConfigEntry):
"""Migrate old entry."""

View File

@@ -90,6 +90,7 @@ CONF_AUTO_REGULATION_SLOW = "auto_regulation_slow"
CONF_AUTO_REGULATION_LIGHT = "auto_regulation_light"
CONF_AUTO_REGULATION_MEDIUM = "auto_regulation_medium"
CONF_AUTO_REGULATION_STRONG = "auto_regulation_strong"
CONF_AUTO_REGULATION_EXPERT = "auto_regulation_expert"
CONF_AUTO_REGULATION_DTEMP = "auto_regulation_dtemp"
CONF_AUTO_REGULATION_PERIOD_MIN = "auto_regulation_periode_min"
CONF_INVERSE_SWITCH = "inverse_switch_command"
@@ -215,6 +216,7 @@ CONF_AUTO_REGULATION_MODES = [
CONF_AUTO_REGULATION_MEDIUM,
CONF_AUTO_REGULATION_STRONG,
CONF_AUTO_REGULATION_SLOW,
CONF_AUTO_REGULATION_EXPERT,
]
CONF_THERMOSTAT_TYPES = [

View File

@@ -160,3 +160,4 @@ set_auto_regulation_mode:
- "Medium"
- "Strong"
- "Slow"
- "Expert"

View File

@@ -352,6 +352,7 @@
"auto_regulation_strong": "Strong",
"auto_regulation_medium": "Medium",
"auto_regulation_light": "Light",
"auto_regulation_expert": "Expert",
"auto_regulation_none": "No auto-regulation"
}
}

View File

@@ -17,6 +17,7 @@ from .pi_algorithm import PITemperatureRegulator
from .const import (
overrides,
DOMAIN,
CONF_CLIMATE,
CONF_CLIMATE_2,
CONF_CLIMATE_3,
@@ -27,6 +28,7 @@ from .const import (
CONF_AUTO_REGULATION_LIGHT,
CONF_AUTO_REGULATION_MEDIUM,
CONF_AUTO_REGULATION_STRONG,
CONF_AUTO_REGULATION_EXPERT,
CONF_AUTO_REGULATION_DTEMP,
CONF_AUTO_REGULATION_PERIOD_MIN,
RegulationParamSlow,
@@ -35,6 +37,7 @@ from .const import (
RegulationParamStrong,
)
from .vtherm_api import VersatileThermostatAPI
from .underlyings import UnderlyingClimate
_LOGGER = logging.getLogger(__name__)
@@ -241,7 +244,34 @@ class ThermostatOverClimate(BaseThermostat):
RegulationParamSlow.stabilization_threshold,
RegulationParamSlow.accumulated_error_threshold,
)
else:
elif self._auto_regulation_mode == CONF_AUTO_REGULATION_EXPERT:
api: VersatileThermostatAPI = VersatileThermostatAPI.get_vtherm_api(
self._hass
)
if api is not None:
if (expert_param := api.self_regulation_expert) is not None:
self._regulation_algo = PITemperatureRegulator(
self.target_temperature,
expert_param.get("kp"),
expert_param.get("ki"),
expert_param.get("k_ext"),
expert_param.get("offset_max"),
expert_param.get("stabilization_threshold"),
expert_param.get("accumulated_error_threshold"),
)
else:
_LOGGER.error(
"%s - Cannot initialize Expert self-regulation mode due to VTherm API doesn't exists. Please contact the publisher of the integration",
self,
)
else:
_LOGGER.error(
"%s - Cannot initialize Expert self-regulation mode cause the configuration in configuration.yaml have not been found. Please see readme documentation for %s",
self,
DOMAIN,
)
if not self._regulation_algo:
# A default empty algo (which does nothing)
self._regulation_algo = PITemperatureRegulator(
self.target_temperature, 0, 0, 0, 0, 0.1, 0
@@ -748,6 +778,8 @@ class ThermostatOverClimate(BaseThermostat):
self.choose_auto_regulation_mode(CONF_AUTO_REGULATION_STRONG)
elif auto_regulation_mode == "Slow":
self.choose_auto_regulation_mode(CONF_AUTO_REGULATION_SLOW)
elif auto_regulation_mode == "Expert":
self.choose_auto_regulation_mode(CONF_AUTO_REGULATION_EXPERT)
await self._send_regulated_temperature()
self.update_custom_attributes()

View File

@@ -352,6 +352,7 @@
"auto_regulation_strong": "Strong",
"auto_regulation_medium": "Medium",
"auto_regulation_light": "Light",
"auto_regulation_expert": "Expert",
"auto_regulation_none": "No auto-regulation"
}
}

View File

@@ -353,6 +353,7 @@
"auto_regulation_strong": "Forte",
"auto_regulation_medium": "Moyenne",
"auto_regulation_light": "Légère",
"auto_regulation_expert": "Expert",
"auto_regulation_none": "Aucune"
}
}

View File

@@ -330,6 +330,7 @@
"auto_regulation_strong": "Forte",
"auto_regulation_medium": "Media",
"auto_regulation_light": "Leggera",
"auto_regulation_expert": "Esperto",
"auto_regulation_none": "Nessuna autoregolamentazione"
}
}

View File

@@ -352,6 +352,7 @@
"auto_regulation_strong": "Strong",
"auto_regulation_medium": "Medium",
"auto_regulation_light": "Light",
"auto_regulation_expert": "Expert",
"auto_regulation_none": "No auto-regulation"
}
}

View File

@@ -0,0 +1,70 @@
""" The API of Versatile Thermostat"""
import logging
from homeassistant.core import HomeAssistant
from homeassistant.config_entries import ConfigEntry
from .const import (
DOMAIN,
CONF_AUTO_REGULATION_EXPERT,
)
VTHERM_API_NAME = "vtherm_api"
_LOGGER = logging.getLogger(__name__)
class VersatileThermostatAPI(dict):
"""The VersatileThermostatAPI"""
_hass: HomeAssistant
# _entries: Dict(str, ConfigEntry)
@classmethod
def get_vtherm_api(cls, hass: HomeAssistant):
"""Get the eventual VTherm API class instance"""
ret = hass.data.get(DOMAIN).get(VTHERM_API_NAME)
if ret is None:
ret = VersatileThermostatAPI(hass)
hass.data[DOMAIN][VTHERM_API_NAME] = ret
return ret
def __init__(self, hass: HomeAssistant) -> None:
_LOGGER.debug("building a VersatileThermostatAPI")
super().__init__()
self._hass = hass
self._expert_params = None
def add_entry(self, entry: ConfigEntry):
"""Add a new entry"""
_LOGGER.debug("Add the entry %s", entry.entry_id)
# self._entries[entry.entry_id] = entry
# Add the entry in hass.data
self._hass.data[DOMAIN][entry.entry_id] = entry
def remove_entry(self, entry: ConfigEntry):
"""Remove an entry"""
_LOGGER.debug("Remove the entry %s", entry.entry_id)
# self._entries.pop(entry.entry_id)
self._hass.data[DOMAIN].pop(entry.entry_id)
# If not more entries are preset, remove the API
if len(self) == 0:
_LOGGER.debug("No more entries-> Remove the API from DOMAIN")
self._hass.data.pop(DOMAIN)
def set_global_config(self, config):
"""Read the global configuration from configuration.yaml file"""
_LOGGER.info("Read global config from configuration.yaml")
self._expert_params = config.get(CONF_AUTO_REGULATION_EXPERT)
if self._expert_params:
_LOGGER.debug("We have found expert params %s", self._expert_params)
@property
def self_regulation_expert(self):
"""Get the self regulation params"""
return self._expert_params
@property
def hass(self):
"""Get the HomeAssistant object"""
return self._hass