Fonctional before testu. Miss the service call
This commit is contained in:
@@ -168,6 +168,7 @@ recorder:
|
|||||||
- switch
|
- switch
|
||||||
- climate
|
- climate
|
||||||
- sensor
|
- sensor
|
||||||
|
- binary_sensor
|
||||||
|
|
||||||
template:
|
template:
|
||||||
- binary_sensor:
|
- binary_sensor:
|
||||||
|
|||||||
@@ -105,6 +105,9 @@ async def reload_all_vtherm(hass):
|
|||||||
]
|
]
|
||||||
|
|
||||||
await asyncio.gather(*reload_tasks)
|
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:
|
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)
|
await reload_all_vtherm(hass)
|
||||||
else:
|
else:
|
||||||
await hass.config_entries.async_reload(entry.entry_id)
|
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:
|
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 unload_ok := await hass.config_entries.async_unload_platforms(entry, PLATFORMS):
|
||||||
if api:
|
if api:
|
||||||
api.remove_entry(entry)
|
api.remove_entry(entry)
|
||||||
|
await api.reload_central_boiler_entities_list()
|
||||||
|
|
||||||
return unload_ok
|
return unload_ok
|
||||||
|
|
||||||
|
|||||||
@@ -283,6 +283,7 @@ class BaseThermostat(ClimateEntity, RestoreEntity):
|
|||||||
|
|
||||||
self._is_central_mode = None
|
self._is_central_mode = None
|
||||||
self._last_central_mode = None
|
self._last_central_mode = None
|
||||||
|
self._is_used_by_central_boiler = False
|
||||||
self.post_init(entry_infos)
|
self.post_init(entry_infos)
|
||||||
|
|
||||||
def clean_central_config_doublon(self, config_entry, central_config) -> dict:
|
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
|
entry_infos.get(CONF_USE_CENTRAL_MODE) is False
|
||||||
) # Default value (None) is True
|
) # Default value (None) is True
|
||||||
|
|
||||||
|
self._is_used_by_central_boiler = (
|
||||||
|
entry_infos.get(CONF_USED_BY_CENTRAL_BOILER) is True
|
||||||
|
)
|
||||||
|
|
||||||
_LOGGER.debug(
|
_LOGGER.debug(
|
||||||
"%s - Creation of a new VersatileThermostat entity: unique_id=%s",
|
"%s - Creation of a new VersatileThermostat entity: unique_id=%s",
|
||||||
self,
|
self,
|
||||||
@@ -878,6 +883,7 @@ class BaseThermostat(ClimateEntity, RestoreEntity):
|
|||||||
|
|
||||||
self.send_event(EventType.PRESET_EVENT, {"preset": self._attr_preset_mode})
|
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_MODE_EVENT, {"hvac_mode": self._hvac_mode})
|
||||||
|
self.send_event(EventType.HVAC_ACTION_EVENT, {"hvac_action": self.hvac_action})
|
||||||
|
|
||||||
_LOGGER.info(
|
_LOGGER.info(
|
||||||
"%s - restored state is target_temp=%.1f, preset_mode=%s, hvac_mode=%s",
|
"%s - restored state is target_temp=%.1f, preset_mode=%s, hvac_mode=%s",
|
||||||
@@ -1010,6 +1016,12 @@ class BaseThermostat(ClimateEntity, RestoreEntity):
|
|||||||
action = HVACAction.HEATING
|
action = HVACAction.HEATING
|
||||||
return action
|
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
|
@property
|
||||||
def target_temperature(self):
|
def target_temperature(self):
|
||||||
"""Return the temperature we try to reach."""
|
"""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:
|
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
|
return
|
||||||
|
|
||||||
if (
|
if (
|
||||||
|
|||||||
@@ -3,11 +3,13 @@
|
|||||||
|
|
||||||
import logging
|
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.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 (
|
from homeassistant.components.binary_sensor import (
|
||||||
BinarySensorEntity,
|
BinarySensorEntity,
|
||||||
@@ -17,6 +19,15 @@ from homeassistant.config_entries import ConfigEntry
|
|||||||
|
|
||||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
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 .commons import VersatileThermostatBaseEntity
|
||||||
from .const import (
|
from .const import (
|
||||||
DOMAIN,
|
DOMAIN,
|
||||||
@@ -28,6 +39,7 @@ from .const import (
|
|||||||
CONF_USE_WINDOW_FEATURE,
|
CONF_USE_WINDOW_FEATURE,
|
||||||
CONF_THERMOSTAT_TYPE,
|
CONF_THERMOSTAT_TYPE,
|
||||||
CONF_THERMOSTAT_CENTRAL_CONFIG,
|
CONF_THERMOSTAT_CENTRAL_CONFIG,
|
||||||
|
overrides,
|
||||||
)
|
)
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
@@ -332,6 +344,8 @@ class CentralBoilerBinarySensor(BinarySensorEntity):
|
|||||||
self._attr_unique_id = "central_boiler_state"
|
self._attr_unique_id = "central_boiler_state"
|
||||||
self._attr_is_on = False
|
self._attr_is_on = False
|
||||||
self._device_name = entry_infos.get(CONF_NAME)
|
self._device_name = entry_infos.get(CONF_NAME)
|
||||||
|
self._entities = []
|
||||||
|
self._hass = hass
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def device_info(self) -> DeviceInfo:
|
def device_info(self) -> DeviceInfo:
|
||||||
@@ -354,3 +368,82 @@ class CentralBoilerBinarySensor(BinarySensorEntity):
|
|||||||
return "mdi:water-boiler"
|
return "mdi:water-boiler"
|
||||||
else:
|
else:
|
||||||
return "mdi:water-boiler-off"
|
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}"
|
||||||
|
|||||||
@@ -281,7 +281,10 @@ class VersatileThermostatBaseConfigFlow(FlowHandler):
|
|||||||
"""Handle the specific main flow steps"""
|
"""Handle the specific main flow steps"""
|
||||||
_LOGGER.debug("Into ConfigFlow.async_step_spec_main user_input=%s", user_input)
|
_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
|
next_step = self.async_step_type
|
||||||
|
|
||||||
self._infos[COMES_FROM] = "async_step_spec_main"
|
self._infos[COMES_FROM] = "async_step_spec_main"
|
||||||
|
|||||||
@@ -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(
|
STEP_CENTRAL_BOILER_SCHEMA = vol.Schema(
|
||||||
{
|
{
|
||||||
vol.Optional(CONF_CENTRAL_BOILER_ACTIVATION_SRV, default=""): str,
|
vol.Optional(CONF_CENTRAL_BOILER_ACTIVATION_SRV, default=""): str,
|
||||||
|
|||||||
@@ -403,6 +403,7 @@ class EventType(Enum):
|
|||||||
POWER_EVENT: str = "versatile_thermostat_power_event"
|
POWER_EVENT: str = "versatile_thermostat_power_event"
|
||||||
TEMPERATURE_EVENT: str = "versatile_thermostat_temperature_event"
|
TEMPERATURE_EVENT: str = "versatile_thermostat_temperature_event"
|
||||||
HVAC_MODE_EVENT: str = "versatile_thermostat_hvac_mode_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"
|
PRESET_EVENT: str = "versatile_thermostat_preset_event"
|
||||||
WINDOW_AUTO_EVENT: str = "versatile_thermostat_window_auto_event"
|
WINDOW_AUTO_EVENT: str = "versatile_thermostat_window_auto_event"
|
||||||
|
|
||||||
|
|||||||
@@ -48,6 +48,7 @@ from .const import (
|
|||||||
AUTO_FAN_DTEMP_THRESHOLD,
|
AUTO_FAN_DTEMP_THRESHOLD,
|
||||||
AUTO_FAN_DEACTIVATED_MODES,
|
AUTO_FAN_DEACTIVATED_MODES,
|
||||||
UnknownEntity,
|
UnknownEntity,
|
||||||
|
EventType,
|
||||||
)
|
)
|
||||||
|
|
||||||
from .vtherm_api import VersatileThermostatAPI
|
from .vtherm_api import VersatileThermostatAPI
|
||||||
@@ -551,6 +552,9 @@ class ThermostatOverClimate(BaseThermostat):
|
|||||||
async def end_climate_changed(changes):
|
async def end_climate_changed(changes):
|
||||||
"""To end the event management"""
|
"""To end the event management"""
|
||||||
if changes:
|
if changes:
|
||||||
|
self.send_event(
|
||||||
|
EventType.HVAC_ACTION_EVENT, {"hvac_action": self.hvac_action}
|
||||||
|
)
|
||||||
self.async_write_ha_state()
|
self.async_write_ha_state()
|
||||||
self.update_custom_attributes()
|
self.update_custom_attributes()
|
||||||
await self.async_control_heating()
|
await self.async_control_heating()
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ from .const import (
|
|||||||
CONF_HEATER_4,
|
CONF_HEATER_4,
|
||||||
CONF_INVERSE_SWITCH,
|
CONF_INVERSE_SWITCH,
|
||||||
overrides,
|
overrides,
|
||||||
|
EventType,
|
||||||
)
|
)
|
||||||
|
|
||||||
from .base_thermostat import BaseThermostat
|
from .base_thermostat import BaseThermostat
|
||||||
@@ -208,5 +209,7 @@ class ThermostatOverSwitch(BaseThermostat):
|
|||||||
return
|
return
|
||||||
if old_state is None:
|
if old_state is None:
|
||||||
self.hass.create_task(self._check_initial_state())
|
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.async_write_ha_state()
|
||||||
self.update_custom_attributes()
|
self.update_custom_attributes()
|
||||||
|
|||||||
@@ -13,7 +13,14 @@ from homeassistant.components.climate import HVACMode
|
|||||||
from .base_thermostat import BaseThermostat
|
from .base_thermostat import BaseThermostat
|
||||||
from .prop_algorithm import PropAlgorithm
|
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
|
from .underlyings import UnderlyingValve
|
||||||
|
|
||||||
@@ -123,6 +130,7 @@ class ThermostatOverValve(BaseThermostat):
|
|||||||
_LOGGER.debug(
|
_LOGGER.debug(
|
||||||
"%s - _async_valve_changed new_state is %s", self, new_state.state
|
"%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
|
@overrides
|
||||||
def update_custom_attributes(self):
|
def update_custom_attributes(self):
|
||||||
|
|||||||
@@ -46,6 +46,7 @@ class VersatileThermostatAPI(dict):
|
|||||||
super().__init__()
|
super().__init__()
|
||||||
self._expert_params = None
|
self._expert_params = None
|
||||||
self._short_ema_params = None
|
self._short_ema_params = None
|
||||||
|
self._central_boiler_entity = None
|
||||||
|
|
||||||
def find_central_configuration(self):
|
def find_central_configuration(self):
|
||||||
"""Search for a central configuration"""
|
"""Search for a central configuration"""
|
||||||
@@ -87,6 +88,16 @@ class VersatileThermostatAPI(dict):
|
|||||||
if self._short_ema_params:
|
if self._short_ema_params:
|
||||||
_LOGGER.debug("We have found short ema params %s", 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
|
@property
|
||||||
def self_regulation_expert(self):
|
def self_regulation_expert(self):
|
||||||
"""Get the self regulation params"""
|
"""Get the self regulation params"""
|
||||||
|
|||||||
Reference in New Issue
Block a user