Skip to content

Commit

Permalink
Deprecate aux_heat in Ecobee integration
Browse files Browse the repository at this point in the history
  • Loading branch information
bjpetit committed May 3, 2024
1 parent 1eed544 commit 0f86a3f
Show file tree
Hide file tree
Showing 7 changed files with 140 additions and 16 deletions.
20 changes: 13 additions & 7 deletions homeassistant/components/ecobee/climate.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,14 @@
from homeassistant.util.unit_conversion import TemperatureConverter

from . import EcobeeData
from .const import _LOGGER, DOMAIN, ECOBEE_MODEL_TO_NAME, MANUFACTURER
from .const import (
_LOGGER,
DOMAIN,
ECOBEE_AUX_HEAT_ONLY,
ECOBEE_MODEL_TO_NAME,
MANUFACTURER,
)
from .repairs import aux_heat_deprecation_issue
from .util import ecobee_date, ecobee_time, is_indefinite_hold

ATTR_COOL_TEMP = "cool_temp"
Expand Down Expand Up @@ -69,17 +76,14 @@
DEFAULT_MAX_HUMIDITY = 50
HUMIDIFIER_MANUAL_MODE = "manual"

ECOBEE_AUX_HEAT_ONLY = "auxHeatOnly"


# Order matters, because for reverse mapping we don't want to map HEAT to AUX
ECOBEE_HVAC_TO_HASS = collections.OrderedDict(
[
("heat", HVACMode.HEAT),
("cool", HVACMode.COOL),
("auto", HVACMode.HEAT_COOL),
("off", HVACMode.OFF),
("auxHeatOnly", HVACMode.HEAT),
(ECOBEE_AUX_HEAT_ONLY, HVACMode.HEAT),
]
)

Expand Down Expand Up @@ -570,15 +574,17 @@ def is_aux_heat(self) -> bool:
"""Return true if aux heater."""
return self.settings["hvacMode"] == ECOBEE_AUX_HEAT_ONLY

def turn_aux_heat_on(self) -> None:
async def async_turn_aux_heat_on(self) -> None:
"""Turn auxiliary heater on."""
aux_heat_deprecation_issue(self.hass)
_LOGGER.debug("Setting HVAC mode to auxHeatOnly to turn on aux heat")
self._last_hvac_mode_before_aux_heat = self.hvac_mode
self.data.ecobee.set_hvac_mode(self.thermostat_index, ECOBEE_AUX_HEAT_ONLY)
self.update_without_throttle = True

def turn_aux_heat_off(self) -> None:
async def async_turn_aux_heat_off(self) -> None:
"""Turn auxiliary heater off."""
aux_heat_deprecation_issue(self.hass)
_LOGGER.debug("Setting HVAC mode to last mode to disable aux heat")
self.set_hvac_mode(self._last_hvac_mode_before_aux_heat)
self.update_without_throttle = True
Expand Down
2 changes: 2 additions & 0 deletions homeassistant/components/ecobee/const.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,8 @@

MANUFACTURER = "ecobee"

ECOBEE_AUX_HEAT_ONLY = "auxHeatOnly"

# Translates ecobee API weatherSymbol to Home Assistant usable names
# https://www.ecobee.com/home/developer/api/documentation/v1/objects/WeatherForecast.shtml
ECOBEE_WEATHER_SYMBOL_TO_HASS = {
Expand Down
17 changes: 16 additions & 1 deletion homeassistant/components/ecobee/repairs.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,26 @@ def migrate_notify_issue(hass: HomeAssistant) -> None:
)


@callback
def aux_heat_deprecation_issue(hass: HomeAssistant) -> None:
"""Ensure an issue is registered."""
ir.async_create_issue(
hass,
DOMAIN,
"migrate_aux_heat",
breaks_in_ha_version="2024.10.0",
is_fixable=True,
is_persistent=True,
severity=ir.IssueSeverity.WARNING,
translation_key="migrate_aux_heat",
)


async def async_create_fix_flow(
hass: HomeAssistant,
issue_id: str,
data: dict[str, str | int | float | None] | None,
) -> RepairsFlow:
"""Create flow."""
assert issue_id == "migrate_notify"
assert issue_id in ["migrate_notify", "migrate_aux_heat"]
return ConfirmRepairFlow()
11 changes: 11 additions & 0 deletions homeassistant/components/ecobee/strings.json
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,17 @@
}
}
}
},
"migrate_aux_heat": {
"title": "Migration of Ecobee aux_heat service",
"fix_flow": {
"step": {
"confirm": {
"description": "The Ecobee `aux_heat` service has been migrated. A new aux_heat_only switch entity per Thermostat is available for thermostats that support auxHeat.\n\nUpdate any automations and dashboards to use the new aux_heat_only switch. When this is done, fix this issue and restart Home Assistant.",
"title": "Disable legacy Ecobee aux_heat service"
}
}
}
}
}
}
50 changes: 46 additions & 4 deletions homeassistant/components/ecobee/switch.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,19 @@

from __future__ import annotations

import logging
from typing import Any

from homeassistant.components.climate import HVACMode
from homeassistant.components.switch import SwitchEntity
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.util import dt as dt_util

from . import EcobeeData
from .const import DOMAIN
from .const import _LOGGER, DOMAIN, ECOBEE_AUX_HEAT_ONLY
from .entity import EcobeeBaseEntity

_LOGGER = logging.getLogger(__name__)

DATE_FORMAT = "%Y-%m-%d %H:%M:%S"


Expand All @@ -37,6 +35,12 @@ async def async_setup_entry(
True,
)

async_add_entities(
EcobeeSwitchAuxHeatOnly(data, index)
for index, thermostat in enumerate(data.ecobee.thermostats)
if thermostat["settings"]["hasHeatPump"]
)


class EcobeeVentilator20MinSwitch(EcobeeBaseEntity, SwitchEntity):
"""A Switch class, representing 20 min timer for an ecobee thermostat with ventilator attached."""
Expand Down Expand Up @@ -88,3 +92,41 @@ async def async_turn_off(self, **kwargs: Any) -> None:
self.data.ecobee.set_ventilator_timer, self.thermostat_index, False
)
self.update_without_throttle = True


class EcobeeSwitchAuxHeatOnly(EcobeeBaseEntity, SwitchEntity):
"""Representation of a aux_heat_only ecobee switch."""

_attr_has_entity_name = True
_attr_name = "Aux Heat Only"

def __init__(
self,
data: EcobeeData,
thermostat_index: int,
) -> None:
"""Initialize ecobee ventilator platform."""
super().__init__(data, thermostat_index)
self._attr_unique_id = f"{self.base_unique_id}_aux_heat_only"
self._attr_is_on = False
self.update_without_throttle = False

self._last_hvac_mode_before_aux_heat = HVACMode.HEAT_COOL

def turn_on(self, **kwargs: Any) -> None:
"""Set the hvacMode to auxHeatOnly."""
_LOGGER.debug("Setting HVAC mode to auxHeatOnly to turn on aux heat")
self._last_hvac_mode_before_aux_heat = self.thermostat["settings"]["hvacMode"]
self.data.ecobee.set_hvac_mode(self.thermostat_index, ECOBEE_AUX_HEAT_ONLY)

def turn_off(self, **kwargs: Any) -> None:
"""Set the hvacMode back to the prior setting."""
_LOGGER.debug("Setting HVAC mode to last mode to disable aux heat")
self.data.ecobee.set_hvac_mode(
self.thermostat_index, self._last_hvac_mode_before_aux_heat
)

@property
def is_on(self) -> bool:
"""Return true if auxHeatOnly mode is active."""
return self.thermostat["settings"]["hvacMode"] == ECOBEE_AUX_HEAT_ONLY
23 changes: 19 additions & 4 deletions tests/components/ecobee/fixtures/ecobee-data.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,14 @@
},
"program": {
"climates": [
{ "name": "Climate1", "climateRef": "c1" },
{ "name": "Climate2", "climateRef": "c2" }
{
"name": "Climate1",
"climateRef": "c1"
},
{
"name": "Climate2",
"climateRef": "c2"
}
],
"currentClimateRef": "c1"
},
Expand All @@ -39,6 +45,7 @@
"isVentilatorTimerOn": false,
"hasHumidifier": true,
"humidifierMode": "manual",
"hasHeatPump": false,
"humidity": "30"
},
"equipmentStatus": "fan",
Expand Down Expand Up @@ -82,8 +89,14 @@
"modelNumber": "athenaSmart",
"program": {
"climates": [
{ "name": "Climate1", "climateRef": "c1" },
{ "name": "Climate2", "climateRef": "c2" }
{
"name": "Climate1",
"climateRef": "c1"
},
{
"name": "Climate2",
"climateRef": "c2"
}
],
"currentClimateRef": "c1"
},
Expand All @@ -109,6 +122,7 @@
"isVentilatorTimerOn": false,
"hasHumidifier": true,
"humidifierMode": "manual",
"hasHeatPump": true,
"humidity": "30"
},
"equipmentStatus": "fan",
Expand Down Expand Up @@ -184,6 +198,7 @@
"isVentilatorTimerOn": false,
"hasHumidifier": true,
"humidifierMode": "manual",
"hasHeatPump": false,
"humidity": "30"
},
"equipmentStatus": "fan",
Expand Down
33 changes: 33 additions & 0 deletions tests/components/ecobee/test_switch.py
Original file line number Diff line number Diff line change
Expand Up @@ -113,3 +113,36 @@ async def test_turn_off_20min_ventilator(hass: HomeAssistant) -> None:
)
await hass.async_block_till_done()
mock_set_20min_ventilator.assert_called_once_with(THERMOSTAT_ID, False)


DEVICE_ID = "switch.ecobee2_aux_heat_only"


async def test_aux_heat_only_turn_on(hass: HomeAssistant) -> None:
"""Test the switch can be turned on."""
with patch("pyecobee.Ecobee.set_hvac_mode") as mock_turn_on:
await setup_platform(hass, DOMAIN)

await hass.services.async_call(
DOMAIN,
SERVICE_TURN_ON,
{ATTR_ENTITY_ID: DEVICE_ID},
blocking=True,
)
await hass.async_block_till_done()
mock_turn_on.assert_called_once_with(1, "auxHeatOnly")


async def test_aux_heat_only_turn_off(hass: HomeAssistant) -> None:
"""Test the switch can be turned off."""
with patch("pyecobee.Ecobee.set_hvac_mode") as mock_turn_off:
await setup_platform(hass, DOMAIN)

await hass.services.async_call(
DOMAIN,
SERVICE_TURN_OFF,
{ATTR_ENTITY_ID: DEVICE_ID},
blocking=True,
)
await hass.async_block_till_done()
mock_turn_off.assert_called_once_with(1, "heat_cool")

0 comments on commit 0f86a3f

Please sign in to comment.