Fonctional before testu. Miss the service call

This commit is contained in:
Jean-Marc Collin
2024-01-14 00:33:52 +00:00
parent 208c80752c
commit 0343d0f0e8
11 changed files with 162 additions and 5 deletions

View File

@@ -168,6 +168,7 @@ recorder:
- switch
- climate
- sensor
- binary_sensor
template:
- binary_sensor:

View File

@@ -105,6 +105,9 @@ async def reload_all_vtherm(hass):
]
await asyncio.gather(*reload_tasks)
api: VersatileThermostatAPI = VersatileThermostatAPI.get_vtherm_api(hass)
if api:
await api.reload_central_boiler_entities_list()
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
@@ -133,6 +136,10 @@ async def update_listener(hass: HomeAssistant, entry: ConfigEntry) -> None:
await reload_all_vtherm(hass)
else:
await hass.config_entries.async_reload(entry.entry_id)
# Reload the central boiler list of entities
api: VersatileThermostatAPI = VersatileThermostatAPI.get_vtherm_api(hass)
if api is not None:
await api.reload_central_boiler_entities_list()
async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
@@ -142,6 +149,7 @@ async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
if unload_ok := await hass.config_entries.async_unload_platforms(entry, PLATFORMS):
if api:
api.remove_entry(entry)
await api.reload_central_boiler_entities_list()
return unload_ok

View File

@@ -283,6 +283,7 @@ class BaseThermostat(ClimateEntity, RestoreEntity):
self._is_central_mode = None
self._last_central_mode = None
self._is_used_by_central_boiler = False
self.post_init(entry_infos)
def clean_central_config_doublon(self, config_entry, central_config) -> dict:
@@ -569,6 +570,10 @@ class BaseThermostat(ClimateEntity, RestoreEntity):
entry_infos.get(CONF_USE_CENTRAL_MODE) is False
) # Default value (None) is True
self._is_used_by_central_boiler = (
entry_infos.get(CONF_USED_BY_CENTRAL_BOILER) is True
)
_LOGGER.debug(
"%s - Creation of a new VersatileThermostat entity: unique_id=%s",
self,
@@ -878,6 +883,7 @@ class BaseThermostat(ClimateEntity, RestoreEntity):
self.send_event(EventType.PRESET_EVENT, {"preset": self._attr_preset_mode})
self.send_event(EventType.HVAC_MODE_EVENT, {"hvac_mode": self._hvac_mode})
self.send_event(EventType.HVAC_ACTION_EVENT, {"hvac_action": self.hvac_action})
_LOGGER.info(
"%s - restored state is target_temp=%.1f, preset_mode=%s, hvac_mode=%s",
@@ -1010,6 +1016,12 @@ class BaseThermostat(ClimateEntity, RestoreEntity):
action = HVACAction.HEATING
return action
@property
def is_used_by_central_boiler(self) -> HVACAction | None:
"""Return true is the VTherm is configured to be used by
central boiler"""
return self._is_used_by_central_boiler
@property
def target_temperature(self):
"""Return the temperature we try to reach."""
@@ -1855,7 +1867,10 @@ class BaseThermostat(ClimateEntity, RestoreEntity):
)
if self.window_bypass_state or not self.is_window_auto_enabled:
_LOGGER.info("%s - Window auto event is ignored because bypass is ON or window auto detection is disabled", self)
_LOGGER.info(
"%s - Window auto event is ignored because bypass is ON or window auto detection is disabled",
self,
)
return
if (

View File

@@ -3,11 +3,13 @@
import logging
from homeassistant.core import HomeAssistant, callback, Event
from homeassistant.core import HomeAssistant, callback, Event, CoreState
from homeassistant.const import STATE_ON, STATE_OFF
from homeassistant.const import STATE_ON, STATE_OFF, EVENT_HOMEASSISTANT_START
from homeassistant.helpers.device_registry import DeviceInfo, DeviceEntryType
from homeassistant.helpers.entity_component import EntityComponent
from homeassistant.helpers.event import async_track_state_change_event
from homeassistant.components.binary_sensor import (
BinarySensorEntity,
@@ -17,6 +19,15 @@ from homeassistant.config_entries import ConfigEntry
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.components.climate import (
ClimateEntity,
HVACMode,
HVACAction,
DOMAIN as CLIMATE_DOMAIN,
)
from custom_components.versatile_thermostat.base_thermostat import BaseThermostat
from .vtherm_api import VersatileThermostatAPI
from .commons import VersatileThermostatBaseEntity
from .const import (
DOMAIN,
@@ -28,6 +39,7 @@ from .const import (
CONF_USE_WINDOW_FEATURE,
CONF_THERMOSTAT_TYPE,
CONF_THERMOSTAT_CENTRAL_CONFIG,
overrides,
)
_LOGGER = logging.getLogger(__name__)
@@ -332,6 +344,8 @@ class CentralBoilerBinarySensor(BinarySensorEntity):
self._attr_unique_id = "central_boiler_state"
self._attr_is_on = False
self._device_name = entry_infos.get(CONF_NAME)
self._entities = []
self._hass = hass
@property
def device_info(self) -> DeviceInfo:
@@ -354,3 +368,82 @@ class CentralBoilerBinarySensor(BinarySensorEntity):
return "mdi:water-boiler"
else:
return "mdi:water-boiler-off"
@overrides
async def async_added_to_hass(self) -> None:
await super().async_added_to_hass()
@callback
async def _async_startup_internal(*_):
_LOGGER.debug("%s - Calling async_startup_internal", self)
api: VersatileThermostatAPI = VersatileThermostatAPI.get_vtherm_api(
self._hass
)
api.register_central_boiler(self)
await self.listen_vtherms_entities()
if self.hass.state == CoreState.running:
await _async_startup_internal()
else:
self.hass.bus.async_listen_once(
EVENT_HOMEASSISTANT_START, _async_startup_internal
)
async def listen_vtherms_entities(self):
"""Initialize the listening of state change of VTherms"""
# Listen to all VTherm state change
self._entities = []
entities_id = []
component: EntityComponent[ClimateEntity] = self.hass.data[CLIMATE_DOMAIN]
for entity in component.entities:
if isinstance(entity, BaseThermostat) and entity.is_used_by_central_boiler:
self._entities.append(entity)
entities_id.append(entity.entity_id)
if len(self._entities) > 0:
# Arme l'écoute de la première entité
listener_cancel = async_track_state_change_event(
self._hass,
entities_id,
self.calculate_central_boiler_state,
)
_LOGGER.info(
"%s - VTherm that could controls the central boiler are %s",
self,
entities_id,
)
self.async_on_remove(listener_cancel)
else:
_LOGGER.debug("%s - no VTherm could controls the central boiler", self)
await self.calculate_central_boiler_state(None)
async def calculate_central_boiler_state(self, _):
"""Calculate the central boiler state depending on all VTherm that
controls this central boiler"""
_LOGGER.debug("%s - calculating the new central boiler state", self)
active = False
for entity in self._entities:
_LOGGER.debug(
"Examining the hvac_action of %s",
entity.name,
)
if (
entity.hvac_mode == HVACMode.HEAT
and entity.hvac_action == HVACAction.HEATING
):
active = True
break
if self._attr_is_on != active:
if active:
_LOGGER.info("%s - turning on the central boiler", self)
else:
_LOGGER.info("%s - turning off the central boiler", self)
self._attr_is_on = active
self.async_write_ha_state()
def __str__(self):
return f"VersatileThermostat-{self.name}"

View File

@@ -281,7 +281,10 @@ class VersatileThermostatBaseConfigFlow(FlowHandler):
"""Handle the specific main flow steps"""
_LOGGER.debug("Into ConfigFlow.async_step_spec_main user_input=%s", user_input)
schema = STEP_CENTRAL_MAIN_DATA_SCHEMA
if self._infos[CONF_THERMOSTAT_TYPE] == CONF_THERMOSTAT_CENTRAL_CONFIG:
schema = STEP_CENTRAL_MAIN_DATA_SCHEMA
else:
schema = STEP_CENTRAL_SPEC_MAIN_DATA_SCHEMA
next_step = self.async_step_type
self._infos[COMES_FROM] = "async_step_spec_main"

View File

@@ -63,6 +63,16 @@ STEP_CENTRAL_MAIN_DATA_SCHEMA = vol.Schema( # pylint: disable=invalid-name
}
)
STEP_CENTRAL_SPEC_MAIN_DATA_SCHEMA = vol.Schema( # pylint: disable=invalid-name
{
vol.Required(CONF_EXTERNAL_TEMP_SENSOR): selector.EntitySelector(
selector.EntitySelectorConfig(domain=[SENSOR_DOMAIN, INPUT_NUMBER_DOMAIN]),
),
vol.Required(CONF_TEMP_MIN, default=7): vol.Coerce(float),
vol.Required(CONF_TEMP_MAX, default=35): vol.Coerce(float),
}
)
STEP_CENTRAL_BOILER_SCHEMA = vol.Schema(
{
vol.Optional(CONF_CENTRAL_BOILER_ACTIVATION_SRV, default=""): str,

View File

@@ -403,6 +403,7 @@ class EventType(Enum):
POWER_EVENT: str = "versatile_thermostat_power_event"
TEMPERATURE_EVENT: str = "versatile_thermostat_temperature_event"
HVAC_MODE_EVENT: str = "versatile_thermostat_hvac_mode_event"
HVAC_ACTION_EVENT: str = "versatile_thermostat_hvac_action_event"
PRESET_EVENT: str = "versatile_thermostat_preset_event"
WINDOW_AUTO_EVENT: str = "versatile_thermostat_window_auto_event"

View File

@@ -48,6 +48,7 @@ from .const import (
AUTO_FAN_DTEMP_THRESHOLD,
AUTO_FAN_DEACTIVATED_MODES,
UnknownEntity,
EventType,
)
from .vtherm_api import VersatileThermostatAPI
@@ -551,6 +552,9 @@ class ThermostatOverClimate(BaseThermostat):
async def end_climate_changed(changes):
"""To end the event management"""
if changes:
self.send_event(
EventType.HVAC_ACTION_EVENT, {"hvac_action": self.hvac_action}
)
self.async_write_ha_state()
self.update_custom_attributes()
await self.async_control_heating()

View File

@@ -13,6 +13,7 @@ from .const import (
CONF_HEATER_4,
CONF_INVERSE_SWITCH,
overrides,
EventType,
)
from .base_thermostat import BaseThermostat
@@ -208,5 +209,7 @@ class ThermostatOverSwitch(BaseThermostat):
return
if old_state is None:
self.hass.create_task(self._check_initial_state())
self.send_event(EventType.HVAC_ACTION_EVENT, {"hvac_action": self.hvac_action})
self.async_write_ha_state()
self.update_custom_attributes()

View File

@@ -13,7 +13,14 @@ from homeassistant.components.climate import HVACMode
from .base_thermostat import BaseThermostat
from .prop_algorithm import PropAlgorithm
from .const import CONF_VALVE, CONF_VALVE_2, CONF_VALVE_3, CONF_VALVE_4, overrides
from .const import (
CONF_VALVE,
CONF_VALVE_2,
CONF_VALVE_3,
CONF_VALVE_4,
overrides,
EventType,
)
from .underlyings import UnderlyingValve
@@ -123,6 +130,7 @@ class ThermostatOverValve(BaseThermostat):
_LOGGER.debug(
"%s - _async_valve_changed new_state is %s", self, new_state.state
)
self.send_event(EventType.HVAC_ACTION_EVENT, {"hvac_action": self.hvac_action})
@overrides
def update_custom_attributes(self):

View File

@@ -46,6 +46,7 @@ class VersatileThermostatAPI(dict):
super().__init__()
self._expert_params = None
self._short_ema_params = None
self._central_boiler_entity = None
def find_central_configuration(self):
"""Search for a central configuration"""
@@ -87,6 +88,16 @@ class VersatileThermostatAPI(dict):
if self._short_ema_params:
_LOGGER.debug("We have found short ema params %s", self._short_ema_params)
def register_central_boiler(self, central_boiler_entity):
"""Register the central boiler entity. This is used by the CentralBoilerBinarySensor
class to register itself at creation"""
self._central_boiler_entity = central_boiler_entity
async def reload_central_boiler_entities_list(self):
"""Reload the central boiler list of entities if a central boiler is used"""
if self._central_boiler_entity is not None:
await self._central_boiler_entity.listen_vtherms_entities()
@property
def self_regulation_expert(self):
"""Get the self regulation params"""