Handle temprorary hold in Honeywell (#128460)

This commit is contained in:
mkmer 2024-10-24 14:07:20 -04:00 committed by Franck Nijhof
parent cc337f7b1e
commit 4a94430bf0
No known key found for this signature in database
GPG Key ID: D62583BA8AB11CA3
2 changed files with 77 additions and 4 deletions

View File

@ -49,6 +49,10 @@ from .const import (
RETRY, RETRY,
) )
MODE_PERMANENT_HOLD = 2
MODE_TEMPORARY_HOLD = 1
MODE_HOLD = {MODE_PERMANENT_HOLD, MODE_TEMPORARY_HOLD}
ATTR_FAN_ACTION = "fan_action" ATTR_FAN_ACTION = "fan_action"
ATTR_PERMANENT_HOLD = "permanent_hold" ATTR_PERMANENT_HOLD = "permanent_hold"
@ -175,6 +179,7 @@ class HoneywellUSThermostat(ClimateEntity):
self._cool_away_temp = cool_away_temp self._cool_away_temp = cool_away_temp
self._heat_away_temp = heat_away_temp self._heat_away_temp = heat_away_temp
self._away = False self._away = False
self._away_hold = False
self._retry = 0 self._retry = 0
self._attr_unique_id = str(device.deviceid) self._attr_unique_id = str(device.deviceid)
@ -323,11 +328,15 @@ class HoneywellUSThermostat(ClimateEntity):
@property @property
def preset_mode(self) -> str | None: def preset_mode(self) -> str | None:
"""Return the current preset mode, e.g., home, away, temp.""" """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 return PRESET_AWAY
if self._is_permanent_hold(): if self._is_hold():
return PRESET_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 return PRESET_NONE
@property @property
@ -335,10 +344,15 @@ class HoneywellUSThermostat(ClimateEntity):
"""Return the fan setting.""" """Return the fan setting."""
return HW_FAN_MODE_TO_HA.get(self._device.fan_mode) 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: def _is_permanent_hold(self) -> bool:
heat_status = self._device.raw_ui_data.get("StatusHeat", 0) heat_status = self._device.raw_ui_data.get("StatusHeat", 0)
cool_status = self._device.raw_ui_data.get("StatusCool", 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: async def _set_temperature(self, **kwargs) -> None:
"""Set new target temperature.""" """Set new target temperature."""

View File

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