Use integration fallback configuration for tado water heater fallback (#111014)

* 103619 tado water heater fallback

* extracted a method to remove code duplication

* test cases and suggested changes

* tests

* util method for connector

* Update homeassistant/components/tado/climate.py

Co-authored-by: Andriy Kushnir <me@orhideous.name>

* missing import after applies suggestion

* early return

* simplify if statements

* simplify pr

* pr requested changes

* better docstring

---------

Co-authored-by: Andriy Kushnir <me@orhideous.name>
This commit is contained in:
Ethem Cem Özkan 2024-05-14 19:27:26 +02:00 committed by GitHub
parent ad6e6a1810
commit b684801cae
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 98 additions and 25 deletions

View file

@ -36,8 +36,6 @@ from .const import (
CONST_MODE_OFF, CONST_MODE_OFF,
CONST_MODE_SMART_SCHEDULE, CONST_MODE_SMART_SCHEDULE,
CONST_OVERLAY_MANUAL, CONST_OVERLAY_MANUAL,
CONST_OVERLAY_TADO_DEFAULT,
CONST_OVERLAY_TADO_MODE,
CONST_OVERLAY_TADO_OPTIONS, CONST_OVERLAY_TADO_OPTIONS,
CONST_OVERLAY_TIMER, CONST_OVERLAY_TIMER,
DATA, DATA,
@ -67,6 +65,7 @@ from .const import (
TYPE_HEATING, TYPE_HEATING,
) )
from .entity import TadoZoneEntity from .entity import TadoZoneEntity
from .helper import decide_overlay_mode
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
@ -598,23 +597,12 @@ class TadoClimate(TadoZoneEntity, ClimateEntity):
self._tado.reset_zone_overlay(self.zone_id) self._tado.reset_zone_overlay(self.zone_id)
return return
# If user gave duration then overlay mode needs to be timer overlay_mode = decide_overlay_mode(
if duration: tado=self._tado,
overlay_mode = CONST_OVERLAY_TIMER duration=duration,
# If no duration or timer set to fallback setting overlay_mode=overlay_mode,
if overlay_mode is None: zone_id=self.zone_id,
overlay_mode = ( )
self._tado.fallback
if self._tado.fallback is not None
else CONST_OVERLAY_TADO_MODE
)
# If default is Tado default then look it up
if overlay_mode == CONST_OVERLAY_TADO_DEFAULT:
overlay_mode = (
self._tado_zone_data.default_overlay_termination_type
if self._tado_zone_data.default_overlay_termination_type is not None
else CONST_OVERLAY_TADO_MODE
)
# If we ended up with a timer but no duration, set a default duration # If we ended up with a timer but no duration, set a default duration
if overlay_mode == CONST_OVERLAY_TIMER and duration is None: if overlay_mode == CONST_OVERLAY_TIMER and duration is None:
duration = ( duration = (

View file

@ -0,0 +1,31 @@
"""Helper methods for Tado."""
from . import TadoConnector
from .const import (
CONST_OVERLAY_TADO_DEFAULT,
CONST_OVERLAY_TADO_MODE,
CONST_OVERLAY_TIMER,
)
def decide_overlay_mode(
tado: TadoConnector,
duration: int | None,
zone_id: int,
overlay_mode: str | None = None,
) -> str:
"""Return correct overlay mode based on the action and defaults."""
# If user gave duration then overlay mode needs to be timer
if duration:
return CONST_OVERLAY_TIMER
# If no duration or timer set to fallback setting
if overlay_mode is None:
overlay_mode = tado.fallback or CONST_OVERLAY_TADO_MODE
# If default is Tado default then look it up
if overlay_mode == CONST_OVERLAY_TADO_DEFAULT:
overlay_mode = (
tado.data["zone"][zone_id].default_overlay_termination_type
or CONST_OVERLAY_TADO_MODE
)
return overlay_mode

View file

@ -32,6 +32,7 @@ from .const import (
TYPE_HOT_WATER, TYPE_HOT_WATER,
) )
from .entity import TadoZoneEntity from .entity import TadoZoneEntity
from .helper import decide_overlay_mode
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
@ -277,12 +278,11 @@ class TadoWaterHeater(TadoZoneEntity, WaterHeaterEntity):
self._tado.set_zone_off(self.zone_id, CONST_OVERLAY_MANUAL, TYPE_HOT_WATER) self._tado.set_zone_off(self.zone_id, CONST_OVERLAY_MANUAL, TYPE_HOT_WATER)
return return
overlay_mode = CONST_OVERLAY_MANUAL overlay_mode = decide_overlay_mode(
if duration: tado=self._tado,
overlay_mode = CONST_OVERLAY_TIMER duration=duration,
elif self._tado.fallback: zone_id=self.zone_id,
# Fallback to Smart Schedule at next Schedule switch if we have fallback enabled )
overlay_mode = CONST_OVERLAY_TADO_MODE
_LOGGER.debug( _LOGGER.debug(
"Switching to %s for zone %s (%d) with temperature %s", "Switching to %s for zone %s (%d) with temperature %s",

View file

@ -0,0 +1,54 @@
"""Helper method tests."""
from unittest.mock import patch
from homeassistant.components.tado import TadoConnector
from homeassistant.components.tado.const import (
CONST_OVERLAY_MANUAL,
CONST_OVERLAY_TADO_DEFAULT,
CONST_OVERLAY_TADO_MODE,
CONST_OVERLAY_TIMER,
)
from homeassistant.components.tado.helper import decide_overlay_mode
from homeassistant.core import HomeAssistant
def dummy_tado_connector(hass: HomeAssistant, fallback) -> TadoConnector:
"""Return dummy tado connector."""
return TadoConnector(hass, username="dummy", password="dummy", fallback=fallback)
async def test_overlay_mode_duration_set(hass: HomeAssistant) -> None:
"""Test overlay method selection when duration is set."""
tado = dummy_tado_connector(hass=hass, fallback=CONST_OVERLAY_TADO_MODE)
overlay_mode = decide_overlay_mode(tado=tado, duration="01:00:00", zone_id=1)
# Must select TIMER overlay
assert overlay_mode == CONST_OVERLAY_TIMER
async def test_overlay_mode_next_time_block_fallback(hass: HomeAssistant) -> None:
"""Test overlay method selection when duration is not set."""
integration_fallback = CONST_OVERLAY_TADO_MODE
tado = dummy_tado_connector(hass=hass, fallback=integration_fallback)
overlay_mode = decide_overlay_mode(tado=tado, duration=None, zone_id=1)
# Must fallback to integration wide setting
assert overlay_mode == integration_fallback
async def test_overlay_mode_tado_default_fallback(hass: HomeAssistant) -> None:
"""Test overlay method selection when tado default is selected."""
integration_fallback = CONST_OVERLAY_TADO_DEFAULT
zone_fallback = CONST_OVERLAY_MANUAL
tado = dummy_tado_connector(hass=hass, fallback=integration_fallback)
class MockZoneData:
def __init__(self) -> None:
self.default_overlay_termination_type = zone_fallback
zone_id = 1
zone_data = {"zone": {zone_id: MockZoneData()}}
with patch.dict(tado.data, zone_data):
overlay_mode = decide_overlay_mode(tado=tado, duration=None, zone_id=zone_id)
# Must fallback to zone setting
assert overlay_mode == zone_fallback