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鈥檒l occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Ecobee humidifier #45003

Merged
merged 13 commits into from
Apr 12, 2021
59 changes: 54 additions & 5 deletions homeassistant/components/ecobee/climate.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@
ATTR_FAN_MIN_ON_TIME = "fan_min_on_time"
ATTR_FAN_MODE = "fan_mode"
ATTR_HEAT_TEMP = "heat_temp"
ATTR_HUMIDIFIER_MODE = "humidifier_mode"
ATTR_RESUME_ALL = "resume_all"
ATTR_START_DATE = "start_date"
ATTR_START_TIME = "start_time"
Expand All @@ -68,7 +69,9 @@

DEFAULT_MIN_HUMIDITY = 15
DEFAULT_MAX_HUMIDITY = 50
HUMIDIFIER_AUTO_MODE = "auto"
HUMIDIFIER_MANUAL_MODE = "manual"
HUMIDIFIER_OFF_MODE = "off"


# Order matters, because for reverse mapping we don't want to map HEAT to AUX
Expand Down Expand Up @@ -111,6 +114,7 @@
SERVICE_RESUME_PROGRAM = "resume_program"
SERVICE_SET_FAN_MIN_ON_TIME = "set_fan_min_on_time"
SERVICE_SET_DST_MODE = "set_dst_mode"
SERVICE_SET_HUMIDIFIER_MODE = "set_humidifier_mode"
SERVICE_SET_MIC_MODE = "set_mic_mode"
SERVICE_SET_OCCUPANCY_MODES = "set_occupancy_modes"

Expand Down Expand Up @@ -271,6 +275,12 @@ def resume_program_set_service(service):
"set_dst_mode",
)

platform.async_register_entity_service(
MartinHjelmare marked this conversation as resolved.
Show resolved Hide resolved
SERVICE_SET_HUMIDIFIER_MODE,
{vol.Required(ATTR_HUMIDIFIER_MODE): cv.string},
"set_humidifier_mode",
)

platform.async_register_entity_service(
SERVICE_SET_MIC_MODE,
{vol.Required(ATTR_MIC_ENABLED): cv.boolean},
Expand Down Expand Up @@ -316,6 +326,14 @@ def __init__(self, data, thermostat_index):
for comfort in self.thermostat["program"]["climates"]
}
self._fan_modes = [FAN_AUTO, FAN_ON]

if self.thermostat["settings"]["hasHumidifier"]:
self._humidifier_modes = [
HUMIDIFIER_OFF_MODE,
HUMIDIFIER_AUTO_MODE,
HUMIDIFIER_MANUAL_MODE,
]

self.update_without_throttle = False

async def async_update(self):
Expand Down Expand Up @@ -401,10 +419,20 @@ def target_temperature_high(self):
@property
def has_humidifier_control(self):
"""Return true if humidifier connected to thermostat and set to manual/on mode."""
return (
self.thermostat["settings"]["hasHumidifier"]
and self.thermostat["settings"]["humidifierMode"] == HUMIDIFIER_MANUAL_MODE
)
return self.humidifier_mode == HUMIDIFIER_MANUAL_MODE

@property
def humidifier_modes(self):
"""Return the available humidifier modes."""
return self._humidifier_modes

@property
def humidifier_mode(self) -> str | None:
"""Return the humidifier mode."""
if not self.thermostat["settings"]["hasHumidifier"]:
return None

return self.thermostat["settings"]["humidifierMode"]

@property
def target_humidity(self) -> int | None:
Expand Down Expand Up @@ -523,7 +551,7 @@ def hvac_action(self):
def extra_state_attributes(self):
"""Return device specific state attributes."""
status = self.thermostat["equipmentStatus"]
return {
attrs = {
MartinHjelmare marked this conversation as resolved.
Show resolved Hide resolved
"fan": self.fan,
"climate_mode": self._preset_modes[
self.thermostat["program"]["currentClimateRef"]
Expand All @@ -532,6 +560,12 @@ def extra_state_attributes(self):
"fan_min_on_time": self.thermostat["settings"]["fanMinOnTime"],
}

if self.thermostat["settings"]["hasHumidifier"]:
attrs["humidifier_mode"] = self.humidifier_mode
attrs["humidifier_modes"] = self.humidifier_modes

return attrs

@property
def is_aux_heat(self):
"""Return true if aux heater."""
Expand Down Expand Up @@ -821,6 +855,21 @@ def set_dst_mode(self, dst_enabled):
"""Enable/disable automatic daylight savings time."""
self.data.ecobee.set_dst_mode(self.thermostat_index, dst_enabled)

def set_humidifier_mode(self, humidifier_mode):
"""Set humidifier mode (auto, off, manual)."""
if not self.thermostat["settings"]["hasHumidifier"]:
raise Exception(
"Cannot set humidifier mode since no humidifier is installed."
)
treylok marked this conversation as resolved.
Show resolved Hide resolved

if humidifier_mode.lower() not in (self.humidifier_modes):
raise ValueError(
f"Invalid humidifier_mode value: {humidifier_mode} Valid values are 'auto', 'off', or 'manual'"
)

self.data.ecobee.set_humidifier_mode(self.thermostat_index, humidifier_mode)
self.update_without_throttle = True

def set_mic_mode(self, mic_enabled):
"""Enable/disable Alexa mic (only for Ecobee 4)."""
self.data.ecobee.set_mic_mode(self.thermostat_index, mic_enabled)
Expand Down
10 changes: 10 additions & 0 deletions homeassistant/components/ecobee/services.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,16 @@ set_dst_mode:
description: Enable automatic daylight savings time.
example: "true"

set_humidifier_mode:
description: Set the humidifier mode.
fields:
entity_id:
description: Name(s) of entities to change.
example: "climate.kitchen"
humidifier_mode:
description: Mode to set humidifier (auto, off, or manual).
example: "auto"

set_mic_mode:
description: Enable/disable Alexa mic (only for Ecobee 4).
fields:
Expand Down
38 changes: 38 additions & 0 deletions tests/components/ecobee/test_climate.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ def ecobee_fixture():
"fanMinOnTime": 10,
"heatCoolMinDelta": 50,
"holdAction": "nextTransition",
"hasHumidifier": True,
"humidifierMode": "off",
},
"equipmentStatus": "fan",
"events": [
Expand Down Expand Up @@ -155,6 +157,8 @@ async def test_extra_state_attributes(ecobee_fixture, thermostat):
"climate_mode": "Climate1",
"fan_min_on_time": 10,
"equipment_running": "heatPump2",
"humidifier_modes": ["off", "auto", "manual"],
"humidifier_mode": "off",
} == thermostat.extra_state_attributes

ecobee_fixture["equipmentStatus"] = "auxHeat2"
Expand All @@ -163,7 +167,28 @@ async def test_extra_state_attributes(ecobee_fixture, thermostat):
"climate_mode": "Climate1",
"fan_min_on_time": 10,
"equipment_running": "auxHeat2",
"humidifier_modes": ["off", "auto", "manual"],
"humidifier_mode": "off",
} == thermostat.extra_state_attributes

ecobee_fixture["settings"]["humidifierMode"] = "auto"
assert {
"fan": "off",
"climate_mode": "Climate1",
"fan_min_on_time": 10,
"equipment_running": "auxHeat2",
"humidifier_modes": ["off", "auto", "manual"],
"humidifier_mode": "auto",
} == thermostat.extra_state_attributes

ecobee_fixture["settings"]["hasHumidifier"] = False
assert {
"fan": "off",
"climate_mode": "Climate1",
"fan_min_on_time": 10,
"equipment_running": "auxHeat2",
} == thermostat.extra_state_attributes

ecobee_fixture["equipmentStatus"] = "compCool1"
assert {
"fan": "off",
Expand Down Expand Up @@ -262,6 +287,19 @@ async def test_set_fan_min_on_time(thermostat, data):
data.ecobee.set_fan_min_on_time.assert_has_calls([mock.call(1, 20)])


async def test_set_humidifier_mode(thermostat, data):
"""Test set humidifier mode setter."""
data.reset_mock()
thermostat.set_humidifier_mode("auto")
data.ecobee.set_humidifier_mode.assert_has_calls([mock.call(1, "auto")])
data.reset_mock()
thermostat.set_humidifier_mode("manual")
data.ecobee.set_humidifier_mode.assert_has_calls([mock.call(1, "manual")])
data.reset_mock()
thermostat.set_humidifier_mode("off")
data.ecobee.set_humidifier_mode.assert_has_calls([mock.call(1, "off")])


async def test_resume_program(thermostat, data):
"""Test resume program."""
# False
Expand Down