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

Open
wants to merge 8 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
61 changes: 48 additions & 13 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 @@ -25,7 +24,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 @@ -46,15 +45,24 @@ def calculate_calibration_local(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_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
_cur_target_temp = self.bt_target_temp

_cur_trv_temp_f = convert_to_float(str(_cur_trv_temp_s), self.name, _context)
_cur_trv_temp_f = convert_to_float(str(_cur_trv_temp_s), self.device_name, _context)

_current_trv_calibration = convert_to_float(
str(self.real_trvs[entity_id]["last_calibration"]), self.name, _context
str(self.real_trvs[entity_id]["last_calibration"]), self.device_name, _context
)

if None in (
Expand All @@ -64,7 +72,7 @@ def calculate_calibration_local(self, entity_id) -> Union[float, None]:
_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 calculate_calibration_local(self, entity_id) -> Union[float, None]:
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 @@ -118,7 +135,7 @@ def calculate_calibration_local(self, entity_id) -> Union[float, None]:
_new_trv_calibration = float(self.real_trvs[entity_id]["local_calibration_min"])

_new_trv_calibration = convert_to_float(
str(_new_trv_calibration), self.name, _context
str(_new_trv_calibration), self.device_name, _context
)

_logmsg = (
Expand All @@ -128,7 +145,7 @@ def calculate_calibration_local(self, entity_id) -> Union[float, None]:

_LOGGER.debug(
_logmsg,
self.name,
self.device_name,
entity_id,
_new_trv_calibration,
_cur_external_temp,
Expand All @@ -139,7 +156,7 @@ def calculate_calibration_local(self, entity_id) -> Union[float, None]:
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 @@ -158,6 +175,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 @@ -197,9 +223,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_down_to_half_degree(_calibrated_setpoint)

Expand All @@ -216,7 +251,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