186 files

This commit is contained in:
Home Assistant Version Control
2026-09-14 12:56:59 +00:00
parent 6be1e92941
commit bd8280defe
186 changed files with 14904 additions and 1087 deletions
@@ -152,6 +152,7 @@ class BaseThermostat(ClimateEntity, RestoreEntity, Generic[T]):
self._attr_translation_key = "versatile_thermostat"
self._total_energy = None
self._energy_unit_needs_persistence = False
_LOGGER.debug("%s - _init_ resetting energy to None", self)
# Because energy of climate is calculated in the thermostat we have to keep
@@ -289,12 +290,13 @@ class BaseThermostat(ClimateEntity, RestoreEntity, Generic[T]):
) -> dict[str, Any]:
"""Removes all values from config with are concerned by central_config"""
def clean_one(cfg, schema: vol.Schema):
def clean_one(cfg, schema: vol.Schema, excluded_keys: set[str] | None = None):
"""Clean one schema"""
excluded_keys = excluded_keys or set()
for marker in schema.schema:
# Extract the actual key from Voluptuous Marker objects
key = marker.schema if hasattr(marker, 'schema') else marker
if key in cfg:
if key in cfg and key not in excluded_keys:
del cfg[key]
cfg = config_entry.copy()
@@ -313,7 +315,14 @@ class BaseThermostat(ClimateEntity, RestoreEntity, Generic[T]):
clean_one(cfg, STEP_CENTRAL_MOTION_DATA_SCHEMA)
if cfg.get(CONF_USE_POWER_CENTRAL_CONFIG) is True:
clean_one(cfg, STEP_CENTRAL_POWER_DATA_SCHEMA)
# The central unit may be ``auto``. It only controls how the
# central input sensors are read; each VTherm keeps its own
# display unit (W or kW) for its power and energy sensors.
clean_one(
cfg,
STEP_CENTRAL_POWER_DATA_SCHEMA,
{CONF_POWER_UNIT},
)
if cfg.get(CONF_USE_PRESENCE_CENTRAL_CONFIG) is True:
clean_one(cfg, STEP_CENTRAL_PRESENCE_DATA_SCHEMA)
@@ -411,6 +420,7 @@ class BaseThermostat(ClimateEntity, RestoreEntity, Generic[T]):
self._last_ext_temperature_measure = self.now
self._total_energy = None
self._energy_unit_needs_persistence = False
_LOGGER.debug("%s - post_init_ resetting energy to None", self)
# Read the parameter from configuration.yaml if it exists
@@ -633,6 +643,7 @@ class BaseThermostat(ClimateEntity, RestoreEntity, Generic[T]):
# need_write_state = True
await self.update_states(force=True)
self._persist_energy_unit_if_needed()
self.recalculate()
# check initial state should be done after the current state has been calculated and so after the manager has been updated
@@ -646,8 +657,24 @@ class BaseThermostat(ClimateEntity, RestoreEntity, Generic[T]):
restored
"""
def _persist_energy_unit_if_needed(self) -> None:
"""Persist restored energy in the current power unit when it changed."""
if not self._energy_unit_needs_persistence:
return
_LOGGER.info(
"%s - Rewriting restored energy from its previous unit to %s",
self,
self.power_manager.power_unit,
)
self.update_custom_attributes()
self.async_write_ha_state()
self._energy_unit_needs_persistence = False
async def get_my_previous_state(self):
"""Try to get my previous state"""
self._energy_unit_needs_persistence = False
# Check If we have an old state
old_state = await self.async_get_last_state()
_LOGGER.debug(
@@ -703,7 +730,38 @@ class BaseThermostat(ClimateEntity, RestoreEntity, Generic[T]):
if old_total_energy is None:
# Fallback to root level for backward compatibility
old_total_energy = old_state.attributes.get(ATTR_TOTAL_ENERGY)
self._total_energy = old_total_energy if old_total_energy is not None else 0
# Energy is stored in the unit associated with the configured power unit.
# Legacy states did not record that unit, so use their persisted
# configuration before falling back to the migrated current one.
if old_total_energy is not None:
stored_unit = specific_states.get(ATTR_TOTAL_ENERGY_UNIT)
if stored_unit is None:
stored_unit = old_state.attributes.get("configuration", {}).get(
CONF_POWER_UNIT,
self.power_manager.power_unit,
)
current_unit = to_internal_power_unit(self.power_manager.power_unit) or POWER_UNIT_WATT
stored_unit = to_internal_power_unit(stored_unit)
has_invalid_stored_unit = stored_unit not in (POWER_UNIT_WATT, POWER_UNIT_KILO_WATT)
if has_invalid_stored_unit:
_LOGGER.warning(
"%s - Restored energy has an unsupported unit %s. Using %s",
self,
stored_unit,
self.power_manager.power_unit,
)
stored_unit = current_unit
self._energy_unit_needs_persistence = has_invalid_stored_unit or stored_unit != current_unit
if self._energy_unit_needs_persistence:
_LOGGER.info(
"%s - Restored energy unit %s differs from configured unit %s",
self,
to_legal_power_unit(stored_unit),
self.power_manager.power_unit,
)
self._total_energy = power_to_watts(old_total_energy, stored_unit) if old_total_energy else 0
else:
self._total_energy = 0
_LOGGER.debug(
"%s - get_my_previous_state restored energy is %s",
self,
@@ -1947,7 +2005,11 @@ class BaseThermostat(ClimateEntity, RestoreEntity, Generic[T]):
"temperature_slope": round(self.last_temperature_slope or 0, 3),
"hvac_off_reason": self.hvac_off_reason,
"hvac_mode_reason": self.hvac_mode_reason,
ATTR_TOTAL_ENERGY: self.total_energy,
ATTR_TOTAL_ENERGY: self.power_manager.from_watts(
self.total_energy,
self.power_manager.power_unit,
),
ATTR_TOTAL_ENERGY_UNIT: self.power_manager.power_unit,
"last_change_time_from_vtherm": (
self._last_change_time_from_vtherm.astimezone(self._current_tz).isoformat() if self._last_change_time_from_vtherm is not None else None
),
@@ -1969,6 +2031,7 @@ class BaseThermostat(ClimateEntity, RestoreEntity, Generic[T]):
"max_on_percent": self._max_on_percent,
"have_valve_regulation": self.have_valve_regulation,
"cycle_min": self._cycle_min,
CONF_POWER_UNIT: self.power_manager.power_unit,
},
"preset_temperatures": {
"frost_temp": self._presets.get(VThermPreset.FROST, 0),