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

Handle temprorary hold in Honeywell #128460

Merged
merged 11 commits into from
Oct 24, 2024
Merged
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
22 changes: 18 additions & 4 deletions homeassistant/components/honeywell/climate.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,10 @@
RETRY,
)

MODE_PERMANENT_HOLD = 2
MODE_TEMPORARY_HOLD = 1
MODE_HOLD = {MODE_PERMANENT_HOLD, MODE_TEMPORARY_HOLD}

ATTR_FAN_ACTION = "fan_action"

ATTR_PERMANENT_HOLD = "permanent_hold"
Expand Down Expand Up @@ -175,6 +179,7 @@ def __init__(
self._cool_away_temp = cool_away_temp
self._heat_away_temp = heat_away_temp
self._away = False
self._away_hold = False
self._retry = 0

self._attr_unique_id = str(device.deviceid)
Expand Down Expand Up @@ -323,22 +328,31 @@ def target_temperature_low(self) -> float | None:
@property
def preset_mode(self) -> str | None:
"""Return the current preset mode, e.g., home, away, temp."""
if self._away:
if self._away and self._is_hold():
self._away_hold = True
return PRESET_AWAY
if self._is_permanent_hold():
if self._is_hold():
return PRESET_HOLD

# Someone has changed the stat manually out of hold in away mode
if self._away and self._away_hold:
self._away = False
self._away_hold = False
return PRESET_NONE

@property
def fan_mode(self) -> str | None:
"""Return the fan setting."""
return HW_FAN_MODE_TO_HA.get(self._device.fan_mode)

def _is_hold(self) -> bool:
heat_status = self._device.raw_ui_data.get("StatusHeat", 0)
cool_status = self._device.raw_ui_data.get("StatusCool", 0)
return heat_status in MODE_HOLD or cool_status in MODE_HOLD

def _is_permanent_hold(self) -> bool:
heat_status = self._device.raw_ui_data.get("StatusHeat", 0)
cool_status = self._device.raw_ui_data.get("StatusCool", 0)
return heat_status == 2 or cool_status == 2
return MODE_PERMANENT_HOLD in (heat_status, cool_status)

async def _set_temperature(self, **kwargs) -> None:
"""Set new target temperature."""
Expand Down
59 changes: 59 additions & 0 deletions tests/components/honeywell/test_climate.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

from aiohttp import ClientConnectionError
import aiosomecomfort
from freezegun.api import FrozenDateTimeFactory
import pytest
from syrupy.assertion import SnapshotAssertion
from syrupy.filters import props
Expand All @@ -29,6 +30,8 @@
)
from homeassistant.components.honeywell.climate import (
DOMAIN,
MODE_PERMANENT_HOLD,
MODE_TEMPORARY_HOLD,
PRESET_HOLD,
RETRY,
SCAN_INTERVAL,
Expand Down Expand Up @@ -1207,3 +1210,59 @@ async def test_unique_id(
await init_integration(hass, config_entry)
entity_entry = entity_registry.async_get(f"climate.{device.name}")
assert entity_entry.unique_id == str(device.deviceid)


async def test_preset_mode(
hass: HomeAssistant,
device: MagicMock,
config_entry: er.EntityRegistry,
freezer: FrozenDateTimeFactory,
) -> None:
"""Test mode settings properly reflected."""
await init_integration(hass, config_entry)
entity_id = f"climate.{device.name}"

device.raw_ui_data["StatusHeat"] = 3
device.raw_ui_data["StatusCool"] = 3

freezer.tick(SCAN_INTERVAL)
async_fire_time_changed(hass)
await hass.async_block_till_done()
state = hass.states.get(entity_id)
assert state.attributes[ATTR_PRESET_MODE] == PRESET_NONE

device.raw_ui_data["StatusHeat"] = MODE_TEMPORARY_HOLD
device.raw_ui_data["StatusCool"] = MODE_TEMPORARY_HOLD

freezer.tick(SCAN_INTERVAL)
async_fire_time_changed(hass)
await hass.async_block_till_done()
state = hass.states.get(entity_id)
assert state.attributes[ATTR_PRESET_MODE] == PRESET_HOLD

device.raw_ui_data["StatusHeat"] = MODE_PERMANENT_HOLD
device.raw_ui_data["StatusCool"] = MODE_PERMANENT_HOLD

freezer.tick(SCAN_INTERVAL)
async_fire_time_changed(hass)
await hass.async_block_till_done()
state = hass.states.get(entity_id)
assert state.attributes[ATTR_PRESET_MODE] == PRESET_HOLD

await hass.services.async_call(
CLIMATE_DOMAIN,
SERVICE_SET_PRESET_MODE,
{ATTR_ENTITY_ID: entity_id, ATTR_PRESET_MODE: PRESET_AWAY},
blocking=True,
)

state = hass.states.get(entity_id)
assert state.attributes[ATTR_PRESET_MODE] == PRESET_AWAY

device.raw_ui_data["StatusHeat"] = 3
device.raw_ui_data["StatusCool"] = 3
freezer.tick(SCAN_INTERVAL)
async_fire_time_changed(hass)
await hass.async_block_till_done()
state = hass.states.get(entity_id)
assert state.attributes[ATTR_PRESET_MODE] == PRESET_NONE