Compare commits

..

1 Commits

Author SHA1 Message Date
Jean-Marc Collin
9102b09691 Calculate offset_calibration as room_temp - local_temp
Fix hvac_action calculation
2024-11-19 06:58:20 +00:00
6 changed files with 54 additions and 15 deletions

View File

@@ -504,7 +504,7 @@ class BaseThermostat(ClimateEntity, RestoreEntity, Generic[T]):
entry_infos.get(CONF_WINDOW_ACTION) or CONF_WINDOW_TURN_OFF entry_infos.get(CONF_WINDOW_ACTION) or CONF_WINDOW_TURN_OFF
) )
self._max_on_percent = api._max_on_percent self._max_on_percent = api.max_on_percent
_LOGGER.debug( _LOGGER.debug(
"%s - Creation of a new VersatileThermostat entity: unique_id=%s", "%s - Creation of a new VersatileThermostat entity: unique_id=%s",

View File

@@ -464,9 +464,9 @@ class RegulationParamVeryStrong:
kp: float = 0.6 kp: float = 0.6
ki: float = 0.1 ki: float = 0.1
k_ext: float = 0.2 k_ext: float = 0.2
offset_max: float = 4 offset_max: float = 8
stabilization_threshold: float = 0.1 stabilization_threshold: float = 0.1
accumulated_error_threshold: float = 30 accumulated_error_threshold: float = 80
class EventType(Enum): class EventType(Enum):

View File

@@ -151,15 +151,13 @@ class ThermostatOverClimate(BaseThermostat[UnderlyingClimate]):
"""True if the Thermostat is over_climate""" """True if the Thermostat is over_climate"""
return True return True
@property def calculate_hvac_action(self, under_list: list) -> HVACAction | None:
def hvac_action(self) -> HVACAction | None: """Calculate an hvac action based on the hvac_action of the list in argument"""
"""Returns the current hvac_action by checking all hvac_action of the underlyings"""
# if one not IDLE or OFF -> return it # if one not IDLE or OFF -> return it
# else if one IDLE -> IDLE # else if one IDLE -> IDLE
# else OFF # else OFF
one_idle = False one_idle = False
for under in self._underlyings: for under in under_list:
if (action := under.hvac_action) not in [ if (action := under.hvac_action) not in [
HVACAction.IDLE, HVACAction.IDLE,
HVACAction.OFF, HVACAction.OFF,
@@ -171,6 +169,11 @@ class ThermostatOverClimate(BaseThermostat[UnderlyingClimate]):
return HVACAction.IDLE return HVACAction.IDLE
return HVACAction.OFF return HVACAction.OFF
@property
def hvac_action(self) -> HVACAction | None:
"""Returns the current hvac_action by checking all hvac_action of the underlyings"""
return self.calculate_hvac_action(self._underlyings)
@overrides @overrides
async def _async_internal_set_temperature(self, temperature: float): async def _async_internal_set_temperature(self, temperature: float):
"""Set the target temperature and the target temperature of underlying climate if any""" """Set the target temperature and the target temperature of underlying climate if any"""

View File

@@ -5,7 +5,7 @@ import logging
from datetime import datetime from datetime import datetime
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from homeassistant.components.climate import HVACMode from homeassistant.components.climate import HVACMode, HVACAction
from .underlyings import UnderlyingSonoffTRVZB from .underlyings import UnderlyingSonoffTRVZB
@@ -96,6 +96,7 @@ class ThermostatOverSonoffTRVZB(ThermostatOverClimate):
offset_calibration_entity_id=offset, offset_calibration_entity_id=offset,
opening_degree_entity_id=opening, opening_degree_entity_id=opening,
closing_degree_entity_id=closing, closing_degree_entity_id=closing,
climate_underlying=self._underlyings[idx],
) )
self._underlyings_sonoff_trvzb.append(under) self._underlyings_sonoff_trvzb.append(under)
@@ -240,3 +241,9 @@ class ThermostatOverSonoffTRVZB(ThermostatOverClimate):
return 0 return 0
else: else:
return self._valve_open_percent return self._valve_open_percent
@property
def hvac_action(self) -> HVACAction | None:
"""Returns the current hvac_action by checking all hvac_action of the _underlyings_sonoff_trvzb"""
return self.calculate_hvac_action(self._underlyings_sonoff_trvzb)

View File

@@ -1030,15 +1030,18 @@ class UnderlyingSonoffTRVZB(UnderlyingValve):
offset_calibration_entity_id: str, offset_calibration_entity_id: str,
opening_degree_entity_id: str, opening_degree_entity_id: str,
closing_degree_entity_id: str, closing_degree_entity_id: str,
climate_underlying: UnderlyingClimate,
) -> None: ) -> None:
"""Initialize the underlying Sonoff TRV""" """Initialize the underlying Sonoff TRV"""
super().__init__(hass, thermostat, opening_degree_entity_id) super().__init__(hass, thermostat, opening_degree_entity_id)
self._offset_calibration_entity_id = offset_calibration_entity_id self._offset_calibration_entity_id = offset_calibration_entity_id
self._opening_degree_entity_id = opening_degree_entity_id self._opening_degree_entity_id = opening_degree_entity_id
self._closing_degree_entity_id = closing_degree_entity_id self._closing_degree_entity_id = closing_degree_entity_id
self._climate_underlying = climate_underlying
self._is_min_max_initialized = False self._is_min_max_initialized = False
self._max_opening_degree = None self._max_opening_degree = None
self._min_offset_calibration = None self._min_offset_calibration = None
self._max_offset_calibration = None
async def send_percent_open(self): async def send_percent_open(self):
"""Send the percent open to the underlying valve""" """Send the percent open to the underlying valve"""
@@ -1052,6 +1055,9 @@ class UnderlyingSonoffTRVZB(UnderlyingValve):
self._min_offset_calibration = self._hass.states.get( self._min_offset_calibration = self._hass.states.get(
self._offset_calibration_entity_id self._offset_calibration_entity_id
).attributes.get("min") ).attributes.get("min")
self._max_offset_calibration = self._hass.states.get(
self._offset_calibration_entity_id
).attributes.get("max")
self._is_min_max_initialized = ( self._is_min_max_initialized = (
self._max_opening_degree is not None self._max_opening_degree is not None
@@ -1067,15 +1073,32 @@ class UnderlyingSonoffTRVZB(UnderlyingValve):
# Send opening_degree # Send opening_degree
await super().send_percent_open() await super().send_percent_open()
# Send closing_degree. TODO 100 hard-coded or take the max of the _closing_degree_entity_id ? # Send closing_degree.
await self._send_value_to_number( await self._send_value_to_number(
self._closing_degree_entity_id, self._closing_degree_entity_id,
self._max_opening_degree - self._percent_open, closing_degree := self._max_opening_degree - self._percent_open,
) )
# send offset_calibration to the min value # send offset_calibration to the difference between target temp and local temp
await self._send_value_to_number( offset = 0
self._offset_calibration_entity_id, self._min_offset_calibration if (
local_temp := self._climate_underlying.underlying_current_temperature
) is not None and (
room_temp := self._thermostat.current_temperature
) is not None:
offset = min(
self._max_offset_calibration,
max(self._min_offset_calibration, room_temp - local_temp),
)
await self._send_value_to_number(self._offset_calibration_entity_id, offset)
_LOGGER.debug(
"%s - SonoffTRVZB - I have sent offset_calibration=%s opening_degree=%s closing_degree=%s",
self,
offset,
self._percent_open,
closing_degree,
) )
@property @property

View File

@@ -179,7 +179,8 @@ class VersatileThermostatAPI(dict):
# ): # ):
# await entity.init_presets(self.find_central_configuration()) # await entity.init_presets(self.find_central_configuration())
# A little hack to test if the climate is a VTherm. Cannot use isinstance due to circular dependency of BaseThermostat # A little hack to test if the climate is a VTherm. Cannot use isinstance
# due to circular dependency of BaseThermostat
if ( if (
entity.device_info entity.device_info
and entity.device_info.get("model", None) == DOMAIN and entity.device_info.get("model", None) == DOMAIN
@@ -249,6 +250,11 @@ class VersatileThermostatAPI(dict):
"""Get the safety_mode params""" """Get the safety_mode params"""
return self._safety_mode return self._safety_mode
@property
def max_on_percent(self):
"""Get the max_open_percent params"""
return self._max_on_percent
@property @property
def central_boiler_entity(self): def central_boiler_entity(self):
"""Get the central boiler binary_sensor entity""" """Get the central boiler binary_sensor entity"""