Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/ha compatibility #1447

Merged
merged 12 commits into from
Jan 3, 2025
15 changes: 10 additions & 5 deletions custom_components/better_thermostat/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,14 +25,17 @@
config_entry_update_listener_lock = Lock()


async def async_setup(hass: HomeAssistant, config: ConfigType):
"""Set up this integration using YAML is not supported."""
hass.data[DOMAIN] = {}
async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
"""Set up this integration using YAML."""
if DOMAIN in config:
hass.data.setdefault(DOMAIN, {})
return True


async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
hass.data[DOMAIN] = {}
"""Set up entry."""
hass.data.setdefault(DOMAIN, {})
hass.data[DOMAIN][entry.entry_id] = {}
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
entry.async_on_unload(entry.add_update_listener(config_entry_update_listener))
return True
Expand All @@ -44,9 +47,11 @@ async def config_entry_update_listener(hass: HomeAssistant, entry: ConfigEntry)
await hass.config_entries.async_reload(entry.entry_id)


async def async_unload_entry(hass, entry):
async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
"""Unload a config entry."""
unload_ok = await hass.config_entries.async_unload_platforms(entry, PLATFORMS)
if unload_ok:
hass.data[DOMAIN].pop(entry.entry_id)
return unload_ok


Expand Down
12 changes: 5 additions & 7 deletions custom_components/better_thermostat/adapters/delegate.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,30 +7,28 @@
async def load_adapter(self, integration, entity_id, get_name=False):
"""Load adapter."""
if get_name:
self.name = "-"
self.device_name = "-"

if integration == "generic_thermostat":
integration = "generic"

try:
self.adapter = await async_import_module(
self.hass,
"custom_components.better_thermostat.adapters." + integration,
self.hass, "custom_components.better_thermostat.adapters." + integration
)
_LOGGER.debug(
"better_thermostat %s: uses adapter %s for trv %s",
self.name,
self.device_name,
integration,
entity_id,
)
except Exception:
self.adapter = await async_import_module(
self.hass,
"custom_components.better_thermostat.adapters.generic",
self.hass, "custom_components.better_thermostat.adapters.generic"
)
_LOGGER.info(
"better_thermostat %s: integration: %s isn't native supported, feel free to open an issue, fallback adapter %s",
self.name,
self.device_name,
integration,
"generic",
)
Expand Down
6 changes: 3 additions & 3 deletions custom_components/better_thermostat/adapters/generic.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ async def init(self, entity_id):
)
_LOGGER.debug(
"better_thermostat %s: uses local calibration entity %s",
self.name,
self.device_name,
self.real_trvs[entity_id]["local_temperature_calibration_entity"],
)
# Wait for the entity to be available
Expand All @@ -39,7 +39,7 @@ async def init(self, entity_id):
).state in (STATE_UNAVAILABLE, STATE_UNKNOWN, None):
_LOGGER.info(
"better_thermostat %s: waiting for TRV/climate entity with id '%s' to become fully available...",
self.name,
self.device_name,
self.real_trvs[entity_id]["local_temperature_calibration_entity"],
)
await asyncio.sleep(5)
Expand Down Expand Up @@ -119,7 +119,7 @@ async def set_temperature(self, entity_id, temperature):

async def set_hvac_mode(self, entity_id, hvac_mode):
"""Set new target hvac mode."""
_LOGGER.debug("better_thermostat %s: set_hvac_mode %s", self.name, hvac_mode)
_LOGGER.debug("better_thermostat %s: set_hvac_mode %s", self.device_name, hvac_mode)
try:
await self.hass.services.async_call(
"climate",
Expand Down
6 changes: 3 additions & 3 deletions custom_components/better_thermostat/adapters/mqtt.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ async def init(self, entity_id):
)
_LOGGER.debug(
"better_thermostat %s: uses local calibration entity %s",
self.name,
self.device_name,
self.real_trvs[entity_id]["local_temperature_calibration_entity"],
)
# Wait for the entity to be available
Expand All @@ -47,7 +47,7 @@ async def init(self, entity_id):
).state in (STATE_UNAVAILABLE, STATE_UNKNOWN, None):
_LOGGER.info(
"better_thermostat %s: waiting for TRV/climate entity with id '%s' to become fully available...",
self.name,
self.device_name,
self.real_trvs[entity_id]["local_temperature_calibration_entity"],
)
await asyncio.sleep(5)
Expand Down Expand Up @@ -159,7 +159,7 @@ async def set_offset(self, entity_id, offset):
async def set_valve(self, entity_id, valve):
"""Set new target valve."""
_LOGGER.debug(
f"better_thermostat {self.name}: TO TRV {entity_id} set_valve: {valve}"
f"better_thermostat {self.device_name}: TO TRV {entity_id} set_valve: {valve}"
)
await self.hass.services.async_call(
"number",
Expand Down
56 changes: 46 additions & 10 deletions custom_components/better_thermostat/calibration.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
"""Helper functions for the Better Thermostat component."""

import logging
from typing import Union

from homeassistant.components.climate.const import HVACAction

Expand All @@ -24,7 +23,7 @@
_LOGGER = logging.getLogger(__name__)


def calculate_calibration_local(self, entity_id) -> Union[float, None]:
def calculate_calibration_local(self, entity_id) -> float | None:
"""Calculate local delta to adjust the setpoint of the TRV based on the air temperature of the external sensor.

This calibration is for devices with local calibration option, it syncs the current temperature of the TRV to the target temperature of
Expand All @@ -48,6 +47,15 @@ def _convert_to_float(value):
if None in (self.cur_temp, self.bt_target_temp):
return None

# Add tolerance check
_within_tolerance = self.cur_temp >= (
self.bt_target_temp - self.tolerance
) and self.cur_temp <= (self.bt_target_temp + self.tolerance)

if _within_tolerance:
# When within tolerance, don't adjust calibration
return self.real_trvs[entity_id]["last_calibration"]

_cur_trv_temp_s = self.real_trvs[entity_id]["current_temperature"]
_calibration_steps = self.real_trvs[entity_id]["local_calibration_steps"]
_cur_external_temp = self.cur_temp
Expand All @@ -64,7 +72,7 @@ def _convert_to_float(value):
_calibration_steps,
):
_LOGGER.warning(
f"better thermostat {self.name}: {entity_id} Could not calculate local calibration in {_context}:"
f"better thermostat {self.device_name}: {entity_id} Could not calculate local calibration in {_context}:"
f" trv_calibration: {_current_trv_calibration}, trv_temp: {_cur_trv_temp_f}, external_temp: {_cur_external_temp}"
f" calibration_steps: {_calibration_steps}"
)
Expand Down Expand Up @@ -102,9 +110,18 @@ def _convert_to_float(value):
CONF_PROTECT_OVERHEATING, False
)

# Base calibration adjustment considering tolerance
if _cur_external_temp >= _cur_target_temp + self.tolerance:
_new_trv_calibration += (
_cur_external_temp - (_cur_target_temp + self.tolerance)
) * 2.0

# Additional adjustment if overheating protection is enabled
if _overheating_protection is True:
if _cur_external_temp >= _cur_target_temp:
_new_trv_calibration += (_cur_external_temp - _cur_target_temp) * 10.0
if _cur_external_temp >= _cur_target_temp + self.tolerance:
_new_trv_calibration += (
_cur_external_temp - (_cur_target_temp + self.tolerance)
) * 8.0 # Reduced from 10.0 since we already add 2.0

# Adjust based on the steps allowed by the local calibration entity
_new_trv_calibration = round_by_steps(_new_trv_calibration, _calibration_steps)
Expand All @@ -114,6 +131,7 @@ def _convert_to_float(value):
t_max = float(self.real_trvs[entity_id]["local_calibration_max"])
_new_trv_calibration = max(t_min, min(_new_trv_calibration, t_max))


_new_trv_calibration = _convert_to_float(_new_trv_calibration)

_logmsg = (
Expand All @@ -123,7 +141,7 @@ def _convert_to_float(value):

_LOGGER.debug(
_logmsg,
self.name,
self.device_name,
entity_id,
_new_trv_calibration,
_cur_external_temp,
Expand All @@ -134,7 +152,7 @@ def _convert_to_float(value):
return _new_trv_calibration


def calculate_calibration_setpoint(self, entity_id) -> Union[float, None]:
def calculate_calibration_setpoint(self, entity_id) -> float | None:
"""Calculate new setpoint for the TRV based on its own temperature measurement and the air temperature of the external sensor.

This calibration is for devices with no local calibration option, it syncs the target temperature of the TRV to a new target
Expand All @@ -153,6 +171,15 @@ def calculate_calibration_setpoint(self, entity_id) -> Union[float, None]:
if None in (self.cur_temp, self.bt_target_temp):
return None

# Add tolerance check
_within_tolerance = self.cur_temp >= (
self.bt_target_temp - self.tolerance
) and self.cur_temp <= (self.bt_target_temp + self.tolerance)

if _within_tolerance:
# When within tolerance, don't adjust calibration
return self.real_trvs[entity_id]["last_temperature"]

_cur_trv_temp_s = self.real_trvs[entity_id]["current_temperature"]

_cur_external_temp = self.cur_temp
Expand Down Expand Up @@ -193,9 +220,18 @@ def calculate_calibration_setpoint(self, entity_id) -> Union[float, None]:
CONF_PROTECT_OVERHEATING, False
)

# Base calibration adjustment considering tolerance
if _cur_external_temp >= _cur_target_temp + self.tolerance:
_calibrated_setpoint -= (
_cur_external_temp - (_cur_target_temp + self.tolerance)
) * 2.0

# Additional adjustment if overheating protection is enabled
if _overheating_protection is True:
if _cur_external_temp >= _cur_target_temp:
_calibrated_setpoint -= (_cur_external_temp - _cur_target_temp) * 10.0
if _cur_external_temp >= _cur_target_temp + self.tolerance:
_calibrated_setpoint -= (
_cur_external_temp - (_cur_target_temp + self.tolerance)
) * 8.0 # Reduced from 10.0 since we already subtract 2.0

_calibrated_setpoint = round_by_steps(_calibrated_setpoint, _trv_temp_steps)

Expand All @@ -211,7 +247,7 @@ def calculate_calibration_setpoint(self, entity_id) -> Union[float, None]:

_LOGGER.debug(
_logmsg,
self.name,
self.device_name,
entity_id,
_calibrated_setpoint,
_cur_external_temp,
Expand Down
Loading
Loading