Add TURN_OFF and TURN_ON to ClimateEntityFeature (#101673)

* Add ClimateEntityFeature.TURN_OFF

* Fixes

* Fixes

* wording

* Change to services

* Fixing

* Fixing

* Last bits

* Review comments

* Add hvac_modes checks

* Fixes

* Add tests

* Review comments

* Update snapshots

* balboa

* coolmaster

* ecobee

* mqtt

* nest

* plugwise

* smarttub

* whirlpool

* zwave_js

* fix test climate

* test climate

* zwave

* nexia

* nuheat

* venstar

* tado

* smartthings

* self.hvac_modes not None

* more tests

* homekit_controller

* homekit controller snapshot
This commit is contained in:
G Johansson 2024-01-30 15:07:47 +01:00 committed by GitHub
parent cece117c93
commit bc720b48b4
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
31 changed files with 534 additions and 110 deletions

View file

@ -1,6 +1,7 @@
"""Provides functionality to interact with climate devices.""" """Provides functionality to interact with climate devices."""
from __future__ import annotations from __future__ import annotations
import asyncio
from datetime import timedelta from datetime import timedelta
import functools as ft import functools as ft
import logging import logging
@ -34,6 +35,7 @@ from homeassistant.helpers.deprecation import (
) )
from homeassistant.helpers.entity import Entity, EntityDescription from homeassistant.helpers.entity import Entity, EntityDescription
from homeassistant.helpers.entity_component import EntityComponent from homeassistant.helpers.entity_component import EntityComponent
from homeassistant.helpers.entity_platform import EntityPlatform
from homeassistant.helpers.temperature import display_temp as show_temp from homeassistant.helpers.temperature import display_temp as show_temp
from homeassistant.helpers.typing import ConfigType from homeassistant.helpers.typing import ConfigType
from homeassistant.util.unit_conversion import TemperatureConverter from homeassistant.util.unit_conversion import TemperatureConverter
@ -152,8 +154,18 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
) )
await component.async_setup(config) await component.async_setup(config)
component.async_register_entity_service(SERVICE_TURN_ON, {}, "async_turn_on") component.async_register_entity_service(
component.async_register_entity_service(SERVICE_TURN_OFF, {}, "async_turn_off") SERVICE_TURN_ON,
{},
"async_turn_on",
[ClimateEntityFeature.TURN_ON],
)
component.async_register_entity_service(
SERVICE_TURN_OFF,
{},
"async_turn_off",
[ClimateEntityFeature.TURN_OFF],
)
component.async_register_entity_service( component.async_register_entity_service(
SERVICE_SET_HVAC_MODE, SERVICE_SET_HVAC_MODE,
{vol.Required(ATTR_HVAC_MODE): vol.Coerce(HVACMode)}, {vol.Required(ATTR_HVAC_MODE): vol.Coerce(HVACMode)},
@ -288,6 +300,102 @@ class ClimateEntity(Entity, cached_properties=CACHED_PROPERTIES_WITH_ATTR_):
_attr_target_temperature: float | None = None _attr_target_temperature: float | None = None
_attr_temperature_unit: str _attr_temperature_unit: str
__mod_supported_features: ClimateEntityFeature = ClimateEntityFeature(0)
def __getattribute__(self, __name: str) -> Any:
"""Get attribute.
Modify return of `supported_features` to
include `_mod_supported_features` if attribute is set.
"""
if __name != "supported_features":
return super().__getattribute__(__name)
# Convert the supported features to ClimateEntityFeature.
# Remove this compatibility shim in 2025.1 or later.
_supported_features = super().__getattribute__(__name)
if type(_supported_features) is int: # noqa: E721
new_features = ClimateEntityFeature(_supported_features)
self._report_deprecated_supported_features_values(new_features)
# Add automatically calculated ClimateEntityFeature.TURN_OFF/TURN_ON to
# supported features and return it
return _supported_features | super().__getattribute__(
"_ClimateEntity__mod_supported_features"
)
@callback
def add_to_platform_start(
self,
hass: HomeAssistant,
platform: EntityPlatform,
parallel_updates: asyncio.Semaphore | None,
) -> None:
"""Start adding an entity to a platform."""
super().add_to_platform_start(hass, platform, parallel_updates)
def _report_turn_on_off(feature: str, method: str) -> None:
"""Log warning not implemented turn on/off feature."""
report_issue = self._suggest_report_issue()
if feature.startswith("TURN"):
message = (
"Entity %s (%s) does not set ClimateEntityFeature.%s"
" but implements the %s method. Please %s"
)
else:
message = (
"Entity %s (%s) implements HVACMode(s): %s and therefore implicitly"
" supports the %s service without setting the proper"
" ClimateEntityFeature. Please %s"
)
_LOGGER.warning(
message,
self.entity_id,
type(self),
feature,
feature.lower(),
report_issue,
)
# Adds ClimateEntityFeature.TURN_OFF/TURN_ON depending on service calls implemented
# This should be removed in 2025.1.
if not self.supported_features & ClimateEntityFeature.TURN_OFF:
if (
type(self).async_turn_off is not ClimateEntity.async_turn_off
or type(self).turn_off is not ClimateEntity.turn_off
):
# turn_off implicitly supported by implementing turn_off method
_report_turn_on_off("TURN_OFF", "turn_off")
self.__mod_supported_features |= ( # pylint: disable=unused-private-member
ClimateEntityFeature.TURN_OFF
)
elif self.hvac_modes and HVACMode.OFF in self.hvac_modes:
# turn_off implicitly supported by including HVACMode.OFF
_report_turn_on_off("off", "turn_off")
self.__mod_supported_features |= ( # pylint: disable=unused-private-member
ClimateEntityFeature.TURN_OFF
)
if not self.supported_features & ClimateEntityFeature.TURN_ON:
if (
type(self).async_turn_on is not ClimateEntity.async_turn_on
or type(self).turn_on is not ClimateEntity.turn_on
):
# turn_on implicitly supported by implementing turn_on method
_report_turn_on_off("TURN_ON", "turn_on")
self.__mod_supported_features |= ( # pylint: disable=unused-private-member
ClimateEntityFeature.TURN_ON
)
elif self.hvac_modes and any(
_mode != HVACMode.OFF and _mode is not None for _mode in self.hvac_modes
):
# turn_on implicitly supported by including any other HVACMode than HVACMode.OFF
_modes = [_mode for _mode in self.hvac_modes if _mode != HVACMode.OFF]
_report_turn_on_off(", ".join(_modes or []), "turn_on")
self.__mod_supported_features |= ( # pylint: disable=unused-private-member
ClimateEntityFeature.TURN_ON
)
@final @final
@property @property
def state(self) -> str | None: def state(self) -> str | None:
@ -312,7 +420,7 @@ class ClimateEntity(Entity, cached_properties=CACHED_PROPERTIES_WITH_ATTR_):
@property @property
def capability_attributes(self) -> dict[str, Any] | None: def capability_attributes(self) -> dict[str, Any] | None:
"""Return the capability attributes.""" """Return the capability attributes."""
supported_features = self.supported_features_compat supported_features = self.supported_features
temperature_unit = self.temperature_unit temperature_unit = self.temperature_unit
precision = self.precision precision = self.precision
hass = self.hass hass = self.hass
@ -345,7 +453,7 @@ class ClimateEntity(Entity, cached_properties=CACHED_PROPERTIES_WITH_ATTR_):
@property @property
def state_attributes(self) -> dict[str, Any]: def state_attributes(self) -> dict[str, Any]:
"""Return the optional state attributes.""" """Return the optional state attributes."""
supported_features = self.supported_features_compat supported_features = self.supported_features
temperature_unit = self.temperature_unit temperature_unit = self.temperature_unit
precision = self.precision precision = self.precision
hass = self.hass hass = self.hass
@ -625,9 +733,14 @@ class ClimateEntity(Entity, cached_properties=CACHED_PROPERTIES_WITH_ATTR_):
"""Turn auxiliary heater off.""" """Turn auxiliary heater off."""
await self.hass.async_add_executor_job(self.turn_aux_heat_off) await self.hass.async_add_executor_job(self.turn_aux_heat_off)
def turn_on(self) -> None:
"""Turn the entity on."""
raise NotImplementedError
async def async_turn_on(self) -> None: async def async_turn_on(self) -> None:
"""Turn the entity on.""" """Turn the entity on."""
if hasattr(self, "turn_on"): # Forward to self.turn_on if it's been overridden.
if type(self).turn_on is not ClimateEntity.turn_on:
await self.hass.async_add_executor_job(self.turn_on) await self.hass.async_add_executor_job(self.turn_on)
return return
@ -646,9 +759,14 @@ class ClimateEntity(Entity, cached_properties=CACHED_PROPERTIES_WITH_ATTR_):
await self.async_set_hvac_mode(mode) await self.async_set_hvac_mode(mode)
break break
def turn_off(self) -> None:
"""Turn the entity off."""
raise NotImplementedError
async def async_turn_off(self) -> None: async def async_turn_off(self) -> None:
"""Turn the entity off.""" """Turn the entity off."""
if hasattr(self, "turn_off"): # Forward to self.turn_on if it's been overridden.
if type(self).turn_off is not ClimateEntity.turn_off:
await self.hass.async_add_executor_job(self.turn_off) await self.hass.async_add_executor_job(self.turn_off)
return return
@ -661,19 +779,6 @@ class ClimateEntity(Entity, cached_properties=CACHED_PROPERTIES_WITH_ATTR_):
"""Return the list of supported features.""" """Return the list of supported features."""
return self._attr_supported_features return self._attr_supported_features
@property
def supported_features_compat(self) -> ClimateEntityFeature:
"""Return the supported features as ClimateEntityFeature.
Remove this compatibility shim in 2025.1 or later.
"""
features = self.supported_features
if type(features) is int: # noqa: E721
new_features = ClimateEntityFeature(features)
self._report_deprecated_supported_features_values(new_features)
return new_features
return features
@cached_property @cached_property
def min_temp(self) -> float: def min_temp(self) -> float:
"""Return the minimum temperature.""" """Return the minimum temperature."""

View file

@ -163,6 +163,8 @@ class ClimateEntityFeature(IntFlag):
PRESET_MODE = 16 PRESET_MODE = 16
SWING_MODE = 32 SWING_MODE = 32
AUX_HEAT = 64 AUX_HEAT = 64
TURN_OFF = 128
TURN_ON = 256
# These SUPPORT_* constants are deprecated as of Home Assistant 2022.5. # These SUPPORT_* constants are deprecated as of Home Assistant 2022.5.

View file

@ -139,8 +139,12 @@ turn_on:
target: target:
entity: entity:
domain: climate domain: climate
supported_features:
- climate.ClimateEntityFeature.TURN_ON
turn_off: turn_off:
target: target:
entity: entity:
domain: climate domain: climate
supported_features:
- climate.ClimateEntityFeature.TURN_OFF

View file

@ -40,7 +40,7 @@
]), ]),
'max_temp': 32, 'max_temp': 32,
'min_temp': 16, 'min_temp': 16,
'supported_features': <ClimateEntityFeature: 11>, 'supported_features': <ClimateEntityFeature: 395>,
'target_temp_high': 24, 'target_temp_high': 24,
'target_temp_low': 20, 'target_temp_low': 20,
'target_temp_step': 1, 'target_temp_step': 1,

View file

@ -51,7 +51,10 @@ async def test_spa_defaults(
assert state assert state
assert ( assert (
state.attributes["supported_features"] state.attributes["supported_features"]
== ClimateEntityFeature.TARGET_TEMPERATURE | ClimateEntityFeature.PRESET_MODE == ClimateEntityFeature.TARGET_TEMPERATURE
| ClimateEntityFeature.PRESET_MODE
| ClimateEntityFeature.TURN_OFF
| ClimateEntityFeature.TURN_ON
) )
assert state.state == HVACMode.HEAT assert state.state == HVACMode.HEAT
assert state.attributes[ATTR_MIN_TEMP] == 10.0 assert state.attributes[ATTR_MIN_TEMP] == 10.0
@ -71,7 +74,10 @@ async def test_spa_defaults_fake_tscale(
assert state assert state
assert ( assert (
state.attributes["supported_features"] state.attributes["supported_features"]
== ClimateEntityFeature.TARGET_TEMPERATURE | ClimateEntityFeature.PRESET_MODE == ClimateEntityFeature.TARGET_TEMPERATURE
| ClimateEntityFeature.PRESET_MODE
| ClimateEntityFeature.TURN_OFF
| ClimateEntityFeature.TURN_ON
) )
assert state.state == HVACMode.HEAT assert state.state == HVACMode.HEAT
assert state.attributes[ATTR_MIN_TEMP] == 10.0 assert state.attributes[ATTR_MIN_TEMP] == 10.0
@ -174,6 +180,8 @@ async def test_spa_with_blower(hass: HomeAssistant, client: MagicMock) -> None:
== ClimateEntityFeature.TARGET_TEMPERATURE == ClimateEntityFeature.TARGET_TEMPERATURE
| ClimateEntityFeature.PRESET_MODE | ClimateEntityFeature.PRESET_MODE
| ClimateEntityFeature.FAN_MODE | ClimateEntityFeature.FAN_MODE
| ClimateEntityFeature.TURN_OFF
| ClimateEntityFeature.TURN_ON
) )
assert state.state == HVACMode.HEAT assert state.state == HVACMode.HEAT
assert state.attributes[ATTR_MIN_TEMP] == 10.0 assert state.attributes[ATTR_MIN_TEMP] == 10.0

View file

@ -45,7 +45,7 @@
'original_name': None, 'original_name': None,
'platform': 'ccm15', 'platform': 'ccm15',
'previous_unique_id': None, 'previous_unique_id': None,
'supported_features': <ClimateEntityFeature: 41>, 'supported_features': <ClimateEntityFeature: 425>,
'translation_key': None, 'translation_key': None,
'unique_id': '1.1.1.1.0', 'unique_id': '1.1.1.1.0',
'unit_of_measurement': None, 'unit_of_measurement': None,
@ -97,7 +97,7 @@
'original_name': None, 'original_name': None,
'platform': 'ccm15', 'platform': 'ccm15',
'previous_unique_id': None, 'previous_unique_id': None,
'supported_features': <ClimateEntityFeature: 41>, 'supported_features': <ClimateEntityFeature: 425>,
'translation_key': None, 'translation_key': None,
'unique_id': '1.1.1.1.1', 'unique_id': '1.1.1.1.1',
'unit_of_measurement': None, 'unit_of_measurement': None,
@ -125,7 +125,7 @@
]), ]),
'max_temp': 35, 'max_temp': 35,
'min_temp': 7, 'min_temp': 7,
'supported_features': <ClimateEntityFeature: 41>, 'supported_features': <ClimateEntityFeature: 425>,
'swing_mode': 'off', 'swing_mode': 'off',
'swing_modes': list([ 'swing_modes': list([
'off', 'off',
@ -163,7 +163,7 @@
]), ]),
'max_temp': 35, 'max_temp': 35,
'min_temp': 7, 'min_temp': 7,
'supported_features': <ClimateEntityFeature: 41>, 'supported_features': <ClimateEntityFeature: 425>,
'swing_mode': 'off', 'swing_mode': 'off',
'swing_modes': list([ 'swing_modes': list([
'off', 'off',
@ -225,7 +225,7 @@
'original_name': None, 'original_name': None,
'platform': 'ccm15', 'platform': 'ccm15',
'previous_unique_id': None, 'previous_unique_id': None,
'supported_features': <ClimateEntityFeature: 41>, 'supported_features': <ClimateEntityFeature: 425>,
'translation_key': None, 'translation_key': None,
'unique_id': '1.1.1.1.0', 'unique_id': '1.1.1.1.0',
'unit_of_measurement': None, 'unit_of_measurement': None,
@ -277,7 +277,7 @@
'original_name': None, 'original_name': None,
'platform': 'ccm15', 'platform': 'ccm15',
'previous_unique_id': None, 'previous_unique_id': None,
'supported_features': <ClimateEntityFeature: 41>, 'supported_features': <ClimateEntityFeature: 425>,
'translation_key': None, 'translation_key': None,
'unique_id': '1.1.1.1.1', 'unique_id': '1.1.1.1.1',
'unit_of_measurement': None, 'unit_of_measurement': None,
@ -302,7 +302,7 @@
]), ]),
'max_temp': 35, 'max_temp': 35,
'min_temp': 7, 'min_temp': 7,
'supported_features': <ClimateEntityFeature: 41>, 'supported_features': <ClimateEntityFeature: 425>,
'swing_modes': list([ 'swing_modes': list([
'off', 'off',
'on', 'on',
@ -335,7 +335,7 @@
]), ]),
'max_temp': 35, 'max_temp': 35,
'min_temp': 7, 'min_temp': 7,
'supported_features': <ClimateEntityFeature: 41>, 'supported_features': <ClimateEntityFeature: 425>,
'swing_modes': list([ 'swing_modes': list([
'off', 'off',
'on', 'on',

View file

@ -25,7 +25,7 @@ from homeassistant.components.climate.const import (
ClimateEntityFeature, ClimateEntityFeature,
) )
from homeassistant.config_entries import ConfigEntry from homeassistant.config_entries import ConfigEntry
from homeassistant.const import UnitOfTemperature from homeassistant.const import SERVICE_TURN_OFF, SERVICE_TURN_ON, UnitOfTemperature
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from homeassistant.exceptions import ServiceValidationError from homeassistant.exceptions import ServiceValidationError
from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.entity_platform import AddEntitiesCallback
@ -154,7 +154,8 @@ async def test_sync_turn_off(hass: HomeAssistant) -> None:
def _create_tuples(enum: Enum, constant_prefix: str) -> list[tuple[Enum, str]]: def _create_tuples(enum: Enum, constant_prefix: str) -> list[tuple[Enum, str]]:
result = [] result = []
for enum in enum: for enum in enum:
result.append((enum, constant_prefix)) if enum not in [ClimateEntityFeature.TURN_ON, ClimateEntityFeature.TURN_OFF]:
result.append((enum, constant_prefix))
return result return result
@ -355,11 +356,262 @@ def test_deprecated_supported_features_ints(caplog: pytest.LogCaptureFixture) ->
return 1 return 1
entity = MockClimateEntity() entity = MockClimateEntity()
assert entity.supported_features_compat is ClimateEntityFeature(1) assert entity.supported_features is ClimateEntityFeature(1)
assert "MockClimateEntity" in caplog.text assert "MockClimateEntity" in caplog.text
assert "is using deprecated supported features values" in caplog.text assert "is using deprecated supported features values" in caplog.text
assert "Instead it should use" in caplog.text assert "Instead it should use" in caplog.text
assert "ClimateEntityFeature.TARGET_TEMPERATURE" in caplog.text assert "ClimateEntityFeature.TARGET_TEMPERATURE" in caplog.text
caplog.clear() caplog.clear()
assert entity.supported_features_compat is ClimateEntityFeature(1) assert entity.supported_features is ClimateEntityFeature(1)
assert "is using deprecated supported features values" not in caplog.text assert "is using deprecated supported features values" not in caplog.text
async def test_warning_not_implemented_turn_on_off_feature(
hass: HomeAssistant, caplog: pytest.LogCaptureFixture, config_flow_fixture: None
) -> None:
"""Test adding feature flag and warn if missing when methods are set."""
called = []
class MockClimateEntityTest(MockClimateEntity):
"""Mock Climate device."""
def turn_on(self) -> None:
"""Turn on."""
called.append("turn_on")
def turn_off(self) -> None:
"""Turn off."""
called.append("turn_off")
async def async_setup_entry_init(
hass: HomeAssistant, config_entry: ConfigEntry
) -> bool:
"""Set up test config entry."""
await hass.config_entries.async_forward_entry_setups(config_entry, [DOMAIN])
return True
async def async_setup_entry_climate_platform(
hass: HomeAssistant,
config_entry: ConfigEntry,
async_add_entities: AddEntitiesCallback,
) -> None:
"""Set up test climate platform via config entry."""
async_add_entities(
[MockClimateEntityTest(name="test", entity_id="climate.test")]
)
mock_integration(
hass,
MockModule(
"test",
async_setup_entry=async_setup_entry_init,
),
built_in=False,
)
mock_platform(
hass,
"test.climate",
MockPlatform(async_setup_entry=async_setup_entry_climate_platform),
)
config_entry = MockConfigEntry(domain="test")
config_entry.add_to_hass(hass)
assert await hass.config_entries.async_setup(config_entry.entry_id)
await hass.async_block_till_done()
state = hass.states.get("climate.test")
assert state is not None
assert (
"Entity climate.test (<class 'tests.components.climate.test_init."
"test_warning_not_implemented_turn_on_off_feature.<locals>.MockClimateEntityTest'>)"
" does not set ClimateEntityFeature.TURN_OFF but implements the turn_off method."
" Please report it to the author of the 'test' custom integration"
in caplog.text
)
assert (
"Entity climate.test (<class 'tests.components.climate.test_init."
"test_warning_not_implemented_turn_on_off_feature.<locals>.MockClimateEntityTest'>)"
" does not set ClimateEntityFeature.TURN_ON but implements the turn_on method."
" Please report it to the author of the 'test' custom integration"
in caplog.text
)
await hass.services.async_call(
DOMAIN,
SERVICE_TURN_ON,
{
"entity_id": "climate.test",
},
blocking=True,
)
await hass.services.async_call(
DOMAIN,
SERVICE_TURN_OFF,
{
"entity_id": "climate.test",
},
blocking=True,
)
assert len(called) == 2
assert "turn_on" in called
assert "turn_off" in called
async def test_implicit_warning_not_implemented_turn_on_off_feature(
hass: HomeAssistant, caplog: pytest.LogCaptureFixture, config_flow_fixture: None
) -> None:
"""Test adding feature flag and warn if missing when methods are not set.
(implicit by hvac mode)
"""
class MockClimateEntityTest(MockEntity, ClimateEntity):
"""Mock Climate device."""
_attr_temperature_unit = UnitOfTemperature.CELSIUS
@property
def hvac_mode(self) -> HVACMode:
"""Return hvac operation ie. heat, cool mode.
Need to be one of HVACMode.*.
"""
return HVACMode.HEAT
@property
def hvac_modes(self) -> list[HVACMode]:
"""Return the list of available hvac operation modes.
Need to be a subset of HVAC_MODES.
"""
return [HVACMode.OFF, HVACMode.HEAT]
async def async_setup_entry_init(
hass: HomeAssistant, config_entry: ConfigEntry
) -> bool:
"""Set up test config entry."""
await hass.config_entries.async_forward_entry_setups(config_entry, [DOMAIN])
return True
async def async_setup_entry_climate_platform(
hass: HomeAssistant,
config_entry: ConfigEntry,
async_add_entities: AddEntitiesCallback,
) -> None:
"""Set up test climate platform via config entry."""
async_add_entities(
[MockClimateEntityTest(name="test", entity_id="climate.test")]
)
mock_integration(
hass,
MockModule(
"test",
async_setup_entry=async_setup_entry_init,
),
built_in=False,
)
mock_platform(
hass,
"test.climate",
MockPlatform(async_setup_entry=async_setup_entry_climate_platform),
)
config_entry = MockConfigEntry(domain="test")
config_entry.add_to_hass(hass)
assert await hass.config_entries.async_setup(config_entry.entry_id)
await hass.async_block_till_done()
state = hass.states.get("climate.test")
assert state is not None
assert (
"Entity climate.test (<class 'tests.components.climate.test_init."
"test_implicit_warning_not_implemented_turn_on_off_feature.<locals>.MockClimateEntityTest'>)"
" implements HVACMode(s): off and therefore implicitly supports the off service without setting"
" the proper ClimateEntityFeature. Please report it to the author of the 'test' custom integration"
in caplog.text
)
assert (
"Entity climate.test (<class 'tests.components.climate.test_init."
"test_implicit_warning_not_implemented_turn_on_off_feature.<locals>.MockClimateEntityTest'>)"
" implements HVACMode(s): heat and therefore implicitly supports the heat service without setting"
" the proper ClimateEntityFeature. Please report it to the author of the 'test' custom integration"
in caplog.text
)
async def test_no_warning_implemented_turn_on_off_feature(
hass: HomeAssistant, caplog: pytest.LogCaptureFixture, config_flow_fixture: None
) -> None:
"""Test no warning when feature flags are set."""
class MockClimateEntityTest(MockClimateEntity):
"""Mock Climate device."""
_attr_supported_features = (
ClimateEntityFeature.FAN_MODE
| ClimateEntityFeature.PRESET_MODE
| ClimateEntityFeature.SWING_MODE
| ClimateEntityFeature.TURN_OFF
| ClimateEntityFeature.TURN_ON
)
async def async_setup_entry_init(
hass: HomeAssistant, config_entry: ConfigEntry
) -> bool:
"""Set up test config entry."""
await hass.config_entries.async_forward_entry_setups(config_entry, [DOMAIN])
return True
async def async_setup_entry_climate_platform(
hass: HomeAssistant,
config_entry: ConfigEntry,
async_add_entities: AddEntitiesCallback,
) -> None:
"""Set up test climate platform via config entry."""
async_add_entities(
[MockClimateEntityTest(name="test", entity_id="climate.test")]
)
mock_integration(
hass,
MockModule(
"test",
async_setup_entry=async_setup_entry_init,
),
built_in=False,
)
mock_platform(
hass,
"test.climate",
MockPlatform(async_setup_entry=async_setup_entry_climate_platform),
)
config_entry = MockConfigEntry(domain="test")
config_entry.add_to_hass(hass)
assert await hass.config_entries.async_setup(config_entry.entry_id)
await hass.async_block_till_done()
state = hass.states.get("climate.test")
assert state is not None
assert (
"does not set ClimateEntityFeature.TURN_OFF but implements the turn_off method."
not in caplog.text
)
assert (
"does not set ClimateEntityFeature.TURN_ON but implements the turn_on method."
not in caplog.text
)
assert (
"implements HVACMode.off and therefore implicitly implements the off method without setting"
not in caplog.text
)
assert (
"implements HVACMode.heat and therefore implicitly implements the heat method without setting"
not in caplog.text
)

View file

@ -60,12 +60,17 @@ async def test_climate_supported_features(
) -> None: ) -> None:
"""Test the Coolmaster climate supported features.""" """Test the Coolmaster climate supported features."""
assert hass.states.get("climate.l1_100").attributes[ATTR_SUPPORTED_FEATURES] == ( assert hass.states.get("climate.l1_100").attributes[ATTR_SUPPORTED_FEATURES] == (
ClimateEntityFeature.TARGET_TEMPERATURE | ClimateEntityFeature.FAN_MODE ClimateEntityFeature.TARGET_TEMPERATURE
| ClimateEntityFeature.FAN_MODE
| ClimateEntityFeature.TURN_OFF
| ClimateEntityFeature.TURN_ON
) )
assert hass.states.get("climate.l1_101").attributes[ATTR_SUPPORTED_FEATURES] == ( assert hass.states.get("climate.l1_101").attributes[ATTR_SUPPORTED_FEATURES] == (
ClimateEntityFeature.TARGET_TEMPERATURE ClimateEntityFeature.TARGET_TEMPERATURE
| ClimateEntityFeature.FAN_MODE | ClimateEntityFeature.FAN_MODE
| ClimateEntityFeature.SWING_MODE | ClimateEntityFeature.SWING_MODE
| ClimateEntityFeature.TURN_OFF
| ClimateEntityFeature.TURN_ON
) )

View file

@ -9,7 +9,7 @@
]), ]),
'max_temp': 24, 'max_temp': 24,
'min_temp': 4, 'min_temp': 4,
'supported_features': <ClimateEntityFeature: 1>, 'supported_features': <ClimateEntityFeature: 257>,
'target_temp_step': 0.5, 'target_temp_step': 0.5,
'temperature': 20, 'temperature': 20,
}), }),
@ -52,7 +52,7 @@
'original_name': None, 'original_name': None,
'platform': 'devolo_home_control', 'platform': 'devolo_home_control',
'previous_unique_id': None, 'previous_unique_id': None,
'supported_features': <ClimateEntityFeature: 1>, 'supported_features': <ClimateEntityFeature: 257>,
'translation_key': None, 'translation_key': None,
'unique_id': 'Test', 'unique_id': 'Test',
'unit_of_measurement': None, 'unit_of_measurement': None,

View file

@ -98,6 +98,8 @@ async def test_aux_heat_not_supported_by_default(hass: HomeAssistant) -> None:
| ClimateEntityFeature.TARGET_HUMIDITY | ClimateEntityFeature.TARGET_HUMIDITY
| ClimateEntityFeature.TARGET_TEMPERATURE_RANGE | ClimateEntityFeature.TARGET_TEMPERATURE_RANGE
| ClimateEntityFeature.TARGET_TEMPERATURE | ClimateEntityFeature.TARGET_TEMPERATURE
| ClimateEntityFeature.TURN_OFF
| ClimateEntityFeature.TURN_ON
) )
@ -115,6 +117,8 @@ async def test_aux_heat_supported_with_heat_pump(hass: HomeAssistant) -> None:
| ClimateEntityFeature.TARGET_TEMPERATURE_RANGE | ClimateEntityFeature.TARGET_TEMPERATURE_RANGE
| ClimateEntityFeature.TARGET_TEMPERATURE | ClimateEntityFeature.TARGET_TEMPERATURE
| ClimateEntityFeature.AUX_HEAT | ClimateEntityFeature.AUX_HEAT
| ClimateEntityFeature.TURN_OFF
| ClimateEntityFeature.TURN_ON
) )

View file

@ -16,7 +16,7 @@
'home', 'home',
'boost', 'boost',
]), ]),
'supported_features': <ClimateEntityFeature: 17>, 'supported_features': <ClimateEntityFeature: 401>,
'target_temp_step': 0.5, 'target_temp_step': 0.5,
'temperature': 22.0, 'temperature': 22.0,
}), }),
@ -65,7 +65,7 @@
'original_name': None, 'original_name': None,
'platform': 'flexit_bacnet', 'platform': 'flexit_bacnet',
'previous_unique_id': None, 'previous_unique_id': None,
'supported_features': <ClimateEntityFeature: 17>, 'supported_features': <ClimateEntityFeature: 401>,
'translation_key': None, 'translation_key': None,
'unique_id': '0000-0001', 'unique_id': '0000-0001',
'unit_of_measurement': None, 'unit_of_measurement': None,

View file

@ -32,7 +32,7 @@
'none', 'none',
'sleep', 'sleep',
]), ]),
'supported_features': <ClimateEntityFeature: 57>, 'supported_features': <ClimateEntityFeature: 441>,
'swing_mode': 'off', 'swing_mode': 'off',
'swing_modes': list([ 'swing_modes': list([
'off', 'off',
@ -110,7 +110,7 @@
'original_name': None, 'original_name': None,
'platform': 'gree', 'platform': 'gree',
'previous_unique_id': None, 'previous_unique_id': None,
'supported_features': <ClimateEntityFeature: 57>, 'supported_features': <ClimateEntityFeature: 441>,
'translation_key': None, 'translation_key': None,
'unique_id': 'aabbcc112233', 'unique_id': 'aabbcc112233',
'unit_of_measurement': None, 'unit_of_measurement': None,

View file

@ -3067,7 +3067,7 @@
'original_name': 'HomeW', 'original_name': 'HomeW',
'platform': 'homekit_controller', 'platform': 'homekit_controller',
'previous_unique_id': None, 'previous_unique_id': None,
'supported_features': <ClimateEntityFeature: 7>, 'supported_features': <ClimateEntityFeature: 391>,
'translation_key': None, 'translation_key': None,
'unique_id': '00:00:00:00:00:00_1_16', 'unique_id': '00:00:00:00:00:00_1_16',
'unit_of_measurement': None, 'unit_of_measurement': None,
@ -3089,7 +3089,7 @@
'max_temp': 33.3, 'max_temp': 33.3,
'min_humidity': 20, 'min_humidity': 20,
'min_temp': 7.2, 'min_temp': 7.2,
'supported_features': <ClimateEntityFeature: 7>, 'supported_features': <ClimateEntityFeature: 391>,
'target_temp_high': None, 'target_temp_high': None,
'target_temp_low': None, 'target_temp_low': None,
'temperature': 22.2, 'temperature': 22.2,
@ -3775,7 +3775,7 @@
'original_name': 'HomeW', 'original_name': 'HomeW',
'platform': 'homekit_controller', 'platform': 'homekit_controller',
'previous_unique_id': None, 'previous_unique_id': None,
'supported_features': <ClimateEntityFeature: 7>, 'supported_features': <ClimateEntityFeature: 391>,
'translation_key': None, 'translation_key': None,
'unique_id': '00:00:00:00:00:00_1_16', 'unique_id': '00:00:00:00:00:00_1_16',
'unit_of_measurement': None, 'unit_of_measurement': None,
@ -3797,7 +3797,7 @@
'max_temp': 33.3, 'max_temp': 33.3,
'min_humidity': 20, 'min_humidity': 20,
'min_temp': 7.2, 'min_temp': 7.2,
'supported_features': <ClimateEntityFeature: 7>, 'supported_features': <ClimateEntityFeature: 391>,
'target_temp_high': None, 'target_temp_high': None,
'target_temp_low': None, 'target_temp_low': None,
'temperature': 22.2, 'temperature': 22.2,
@ -4188,7 +4188,7 @@
'original_name': 'HomeW', 'original_name': 'HomeW',
'platform': 'homekit_controller', 'platform': 'homekit_controller',
'previous_unique_id': None, 'previous_unique_id': None,
'supported_features': <ClimateEntityFeature: 7>, 'supported_features': <ClimateEntityFeature: 391>,
'translation_key': None, 'translation_key': None,
'unique_id': '00:00:00:00:00:00_1_16', 'unique_id': '00:00:00:00:00:00_1_16',
'unit_of_measurement': None, 'unit_of_measurement': None,
@ -4210,7 +4210,7 @@
'max_temp': 33.3, 'max_temp': 33.3,
'min_humidity': 20, 'min_humidity': 20,
'min_temp': 7.2, 'min_temp': 7.2,
'supported_features': <ClimateEntityFeature: 7>, 'supported_features': <ClimateEntityFeature: 391>,
'target_temp_high': None, 'target_temp_high': None,
'target_temp_low': None, 'target_temp_low': None,
'temperature': 22.2, 'temperature': 22.2,
@ -4853,7 +4853,7 @@
'original_name': 'My ecobee', 'original_name': 'My ecobee',
'platform': 'homekit_controller', 'platform': 'homekit_controller',
'previous_unique_id': None, 'previous_unique_id': None,
'supported_features': <ClimateEntityFeature: 15>, 'supported_features': <ClimateEntityFeature: 399>,
'translation_key': None, 'translation_key': None,
'unique_id': '00:00:00:00:00:00_1_16', 'unique_id': '00:00:00:00:00:00_1_16',
'unit_of_measurement': None, 'unit_of_measurement': None,
@ -4880,7 +4880,7 @@
'max_temp': 33.3, 'max_temp': 33.3,
'min_humidity': 20, 'min_humidity': 20,
'min_temp': 7.2, 'min_temp': 7.2,
'supported_features': <ClimateEntityFeature: 15>, 'supported_features': <ClimateEntityFeature: 399>,
'target_temp_high': 25.6, 'target_temp_high': 25.6,
'target_temp_low': 7.2, 'target_temp_low': 7.2,
'temperature': None, 'temperature': None,
@ -7004,7 +7004,7 @@
'original_name': '89 Living Room', 'original_name': '89 Living Room',
'platform': 'homekit_controller', 'platform': 'homekit_controller',
'previous_unique_id': None, 'previous_unique_id': None,
'supported_features': 0, 'supported_features': <ClimateEntityFeature: 384>,
'translation_key': None, 'translation_key': None,
'unique_id': '00:00:00:00:00:00_1233851541_169', 'unique_id': '00:00:00:00:00:00_1233851541_169',
'unit_of_measurement': None, 'unit_of_measurement': None,
@ -7022,7 +7022,7 @@
]), ]),
'max_temp': 35, 'max_temp': 35,
'min_temp': 7, 'min_temp': 7,
'supported_features': <ClimateEntityFeature: 0>, 'supported_features': <ClimateEntityFeature: 384>,
'target_temp_step': 1.0, 'target_temp_step': 1.0,
}), }),
'entity_id': 'climate.89_living_room', 'entity_id': 'climate.89_living_room',
@ -8431,7 +8431,7 @@
'original_name': '89 Living Room', 'original_name': '89 Living Room',
'platform': 'homekit_controller', 'platform': 'homekit_controller',
'previous_unique_id': None, 'previous_unique_id': None,
'supported_features': <ClimateEntityFeature: 32>, 'supported_features': <ClimateEntityFeature: 416>,
'translation_key': None, 'translation_key': None,
'unique_id': '00:00:00:00:00:00_1233851541_169', 'unique_id': '00:00:00:00:00:00_1233851541_169',
'unit_of_measurement': None, 'unit_of_measurement': None,
@ -8449,7 +8449,7 @@
]), ]),
'max_temp': 35, 'max_temp': 35,
'min_temp': 7, 'min_temp': 7,
'supported_features': <ClimateEntityFeature: 32>, 'supported_features': <ClimateEntityFeature: 416>,
'swing_mode': 'vertical', 'swing_mode': 'vertical',
'swing_modes': list([ 'swing_modes': list([
'off', 'off',
@ -9509,7 +9509,7 @@
'original_name': 'Air Conditioner SlaveID 1', 'original_name': 'Air Conditioner SlaveID 1',
'platform': 'homekit_controller', 'platform': 'homekit_controller',
'previous_unique_id': None, 'previous_unique_id': None,
'supported_features': <ClimateEntityFeature: 9>, 'supported_features': <ClimateEntityFeature: 393>,
'translation_key': None, 'translation_key': None,
'unique_id': '00:00:00:00:00:00_1_9', 'unique_id': '00:00:00:00:00:00_1_9',
'unit_of_measurement': None, 'unit_of_measurement': None,
@ -9534,7 +9534,7 @@
]), ]),
'max_temp': 32, 'max_temp': 32,
'min_temp': 18, 'min_temp': 18,
'supported_features': <ClimateEntityFeature: 9>, 'supported_features': <ClimateEntityFeature: 393>,
'target_temp_step': 0.5, 'target_temp_step': 0.5,
'temperature': 24.5, 'temperature': 24.5,
}), }),
@ -12059,7 +12059,7 @@
'original_name': 'Lennox', 'original_name': 'Lennox',
'platform': 'homekit_controller', 'platform': 'homekit_controller',
'previous_unique_id': None, 'previous_unique_id': None,
'supported_features': <ClimateEntityFeature: 3>, 'supported_features': <ClimateEntityFeature: 387>,
'translation_key': None, 'translation_key': None,
'unique_id': '00:00:00:00:00:00_1_100', 'unique_id': '00:00:00:00:00:00_1_100',
'unit_of_measurement': None, 'unit_of_measurement': None,
@ -12078,7 +12078,7 @@
]), ]),
'max_temp': 37, 'max_temp': 37,
'min_temp': 4.5, 'min_temp': 4.5,
'supported_features': <ClimateEntityFeature: 3>, 'supported_features': <ClimateEntityFeature: 387>,
'target_temp_high': 29.5, 'target_temp_high': 29.5,
'target_temp_low': 21, 'target_temp_low': 21,
'temperature': None, 'temperature': None,
@ -13027,7 +13027,7 @@
'original_name': 'Mysa-85dda9 Thermostat', 'original_name': 'Mysa-85dda9 Thermostat',
'platform': 'homekit_controller', 'platform': 'homekit_controller',
'previous_unique_id': None, 'previous_unique_id': None,
'supported_features': <ClimateEntityFeature: 1>, 'supported_features': <ClimateEntityFeature: 385>,
'translation_key': None, 'translation_key': None,
'unique_id': '00:00:00:00:00:00_1_20', 'unique_id': '00:00:00:00:00:00_1_20',
'unit_of_measurement': None, 'unit_of_measurement': None,
@ -13046,7 +13046,7 @@
]), ]),
'max_temp': 35, 'max_temp': 35,
'min_temp': 7, 'min_temp': 7,
'supported_features': <ClimateEntityFeature: 1>, 'supported_features': <ClimateEntityFeature: 385>,
'temperature': None, 'temperature': None,
}), }),
'entity_id': 'climate.mysa_85dda9_thermostat', 'entity_id': 'climate.mysa_85dda9_thermostat',

View file

@ -107,6 +107,8 @@ async def test_ecobee3_setup(hass: HomeAssistant) -> None:
ClimateEntityFeature.TARGET_TEMPERATURE ClimateEntityFeature.TARGET_TEMPERATURE
| ClimateEntityFeature.TARGET_TEMPERATURE_RANGE | ClimateEntityFeature.TARGET_TEMPERATURE_RANGE
| ClimateEntityFeature.TARGET_HUMIDITY | ClimateEntityFeature.TARGET_HUMIDITY
| ClimateEntityFeature.TURN_OFF
| ClimateEntityFeature.TURN_ON
), ),
capabilities={ capabilities={
"hvac_modes": ["off", "heat", "cool", "heat_cool"], "hvac_modes": ["off", "heat", "cool", "heat_cool"],

View file

@ -28,7 +28,10 @@ async def test_cover_add_feature_at_runtime(
assert climate.unique_id == "00:00:00:00:00:00_1233851541_169" assert climate.unique_id == "00:00:00:00:00:00_1233851541_169"
climate_state = hass.states.get("climate.89_living_room") climate_state = hass.states.get("climate.89_living_room")
assert climate_state.attributes[ATTR_SUPPORTED_FEATURES] is ClimateEntityFeature(0) assert (
climate_state.attributes[ATTR_SUPPORTED_FEATURES]
is ClimateEntityFeature.TURN_OFF | ClimateEntityFeature.TURN_ON
)
assert ATTR_SWING_MODES not in climate_state.attributes assert ATTR_SWING_MODES not in climate_state.attributes
climate = entity_registry.async_get("climate.89_living_room") climate = entity_registry.async_get("climate.89_living_room")
@ -44,5 +47,7 @@ async def test_cover_add_feature_at_runtime(
assert ( assert (
climate_state.attributes[ATTR_SUPPORTED_FEATURES] climate_state.attributes[ATTR_SUPPORTED_FEATURES]
is ClimateEntityFeature.SWING_MODE is ClimateEntityFeature.SWING_MODE
| ClimateEntityFeature.TURN_OFF
| ClimateEntityFeature.TURN_ON
) )
assert climate_state.attributes[ATTR_SWING_MODES] == ["off", "vertical"] assert climate_state.attributes[ATTR_SWING_MODES] == ["off", "vertical"]

View file

@ -30,7 +30,7 @@
'away', 'away',
'hold', 'hold',
]), ]),
'supported_features': <ClimateEntityFeature: 95>, 'supported_features': <ClimateEntityFeature: 479>,
'target_temp_high': None, 'target_temp_high': None,
'target_temp_low': None, 'target_temp_low': None,
'temperature': None, 'temperature': None,

View file

@ -89,7 +89,10 @@ async def test_setup_thermostat(
assert state.attributes.get(ATTR_PRESET_MODE) == PRESET_NONE assert state.attributes.get(ATTR_PRESET_MODE) == PRESET_NONE
assert ( assert (
state.attributes.get(ATTR_SUPPORTED_FEATURES) state.attributes.get(ATTR_SUPPORTED_FEATURES)
== ClimateEntityFeature.TARGET_TEMPERATURE | ClimateEntityFeature.PRESET_MODE == ClimateEntityFeature.TARGET_TEMPERATURE
| ClimateEntityFeature.PRESET_MODE
| ClimateEntityFeature.TURN_OFF
| ClimateEntityFeature.TURN_ON
) )
assert state.attributes.get(ATTR_MAX_TEMP) == MAX_TEMPERATURE assert state.attributes.get(ATTR_MAX_TEMP) == MAX_TEMPERATURE
assert state.attributes.get(ATTR_MIN_TEMP) == 5.0 assert state.attributes.get(ATTR_MIN_TEMP) == 5.0

View file

@ -226,6 +226,8 @@ async def test_supported_features(
| ClimateEntityFeature.PRESET_MODE | ClimateEntityFeature.PRESET_MODE
| ClimateEntityFeature.TARGET_TEMPERATURE_RANGE | ClimateEntityFeature.TARGET_TEMPERATURE_RANGE
| ClimateEntityFeature.TARGET_HUMIDITY | ClimateEntityFeature.TARGET_HUMIDITY
| ClimateEntityFeature.TURN_OFF
| ClimateEntityFeature.TURN_ON
) )
assert state.attributes.get("supported_features") == support assert state.attributes.get("supported_features") == support
@ -1327,6 +1329,8 @@ async def test_set_aux(
| ClimateEntityFeature.PRESET_MODE | ClimateEntityFeature.PRESET_MODE
| ClimateEntityFeature.TARGET_TEMPERATURE_RANGE | ClimateEntityFeature.TARGET_TEMPERATURE_RANGE
| ClimateEntityFeature.TARGET_HUMIDITY | ClimateEntityFeature.TARGET_HUMIDITY
| ClimateEntityFeature.TURN_OFF
| ClimateEntityFeature.TURN_ON
) )
assert state.attributes.get("supported_features") == support assert state.attributes.get("supported_features") == support

View file

@ -909,6 +909,8 @@ async def test_thermostat_fan_off(
ClimateEntityFeature.TARGET_TEMPERATURE ClimateEntityFeature.TARGET_TEMPERATURE
| ClimateEntityFeature.TARGET_TEMPERATURE_RANGE | ClimateEntityFeature.TARGET_TEMPERATURE_RANGE
| ClimateEntityFeature.FAN_MODE | ClimateEntityFeature.FAN_MODE
| ClimateEntityFeature.TURN_OFF
| ClimateEntityFeature.TURN_ON
) )
@ -956,6 +958,8 @@ async def test_thermostat_fan_on(
ClimateEntityFeature.TARGET_TEMPERATURE ClimateEntityFeature.TARGET_TEMPERATURE
| ClimateEntityFeature.TARGET_TEMPERATURE_RANGE | ClimateEntityFeature.TARGET_TEMPERATURE_RANGE
| ClimateEntityFeature.FAN_MODE | ClimateEntityFeature.FAN_MODE
| ClimateEntityFeature.TURN_OFF
| ClimateEntityFeature.TURN_ON
) )
@ -999,6 +1003,8 @@ async def test_thermostat_cool_with_fan(
ClimateEntityFeature.TARGET_TEMPERATURE ClimateEntityFeature.TARGET_TEMPERATURE
| ClimateEntityFeature.TARGET_TEMPERATURE_RANGE | ClimateEntityFeature.TARGET_TEMPERATURE_RANGE
| ClimateEntityFeature.FAN_MODE | ClimateEntityFeature.FAN_MODE
| ClimateEntityFeature.TURN_OFF
| ClimateEntityFeature.TURN_ON
) )
@ -1036,6 +1042,8 @@ async def test_thermostat_set_fan(
ClimateEntityFeature.TARGET_TEMPERATURE ClimateEntityFeature.TARGET_TEMPERATURE
| ClimateEntityFeature.TARGET_TEMPERATURE_RANGE | ClimateEntityFeature.TARGET_TEMPERATURE_RANGE
| ClimateEntityFeature.FAN_MODE | ClimateEntityFeature.FAN_MODE
| ClimateEntityFeature.TURN_OFF
| ClimateEntityFeature.TURN_ON
) )
# Turn off fan mode # Turn off fan mode
@ -1098,6 +1106,8 @@ async def test_thermostat_set_fan_when_off(
ClimateEntityFeature.TARGET_TEMPERATURE ClimateEntityFeature.TARGET_TEMPERATURE
| ClimateEntityFeature.TARGET_TEMPERATURE_RANGE | ClimateEntityFeature.TARGET_TEMPERATURE_RANGE
| ClimateEntityFeature.FAN_MODE | ClimateEntityFeature.FAN_MODE
| ClimateEntityFeature.TURN_OFF
| ClimateEntityFeature.TURN_ON
) )
# Fan cannot be turned on when HVAC is off # Fan cannot be turned on when HVAC is off
@ -1143,6 +1153,8 @@ async def test_thermostat_fan_empty(
assert thermostat.attributes[ATTR_SUPPORTED_FEATURES] == ( assert thermostat.attributes[ATTR_SUPPORTED_FEATURES] == (
ClimateEntityFeature.TARGET_TEMPERATURE ClimateEntityFeature.TARGET_TEMPERATURE
| ClimateEntityFeature.TARGET_TEMPERATURE_RANGE | ClimateEntityFeature.TARGET_TEMPERATURE_RANGE
| ClimateEntityFeature.TURN_OFF
| ClimateEntityFeature.TURN_ON
) )
# Ignores set_fan_mode since it is lacking SUPPORT_FAN_MODE # Ignores set_fan_mode since it is lacking SUPPORT_FAN_MODE

View file

@ -38,7 +38,7 @@
'original_name': 'Bureau', 'original_name': 'Bureau',
'platform': 'netatmo', 'platform': 'netatmo',
'previous_unique_id': None, 'previous_unique_id': None,
'supported_features': <ClimateEntityFeature: 17>, 'supported_features': <ClimateEntityFeature: 401>,
'translation_key': None, 'translation_key': None,
'unique_id': '222452125-DeviceType.OTM', 'unique_id': '222452125-DeviceType.OTM',
'unit_of_measurement': None, 'unit_of_measurement': None,
@ -61,7 +61,7 @@
'Frost Guard', 'Frost Guard',
'Schedule', 'Schedule',
]), ]),
'supported_features': <ClimateEntityFeature: 17>, 'supported_features': <ClimateEntityFeature: 401>,
'target_temp_step': 0.5, 'target_temp_step': 0.5,
}), }),
'context': <ANY>, 'context': <ANY>,
@ -110,7 +110,7 @@
'original_name': 'Cocina', 'original_name': 'Cocina',
'platform': 'netatmo', 'platform': 'netatmo',
'previous_unique_id': None, 'previous_unique_id': None,
'supported_features': <ClimateEntityFeature: 17>, 'supported_features': <ClimateEntityFeature: 401>,
'translation_key': None, 'translation_key': None,
'unique_id': '2940411577-DeviceType.NRV', 'unique_id': '2940411577-DeviceType.NRV',
'unit_of_measurement': None, 'unit_of_measurement': None,
@ -138,7 +138,7 @@
'Schedule', 'Schedule',
]), ]),
'selected_schedule': 'Default', 'selected_schedule': 'Default',
'supported_features': <ClimateEntityFeature: 17>, 'supported_features': <ClimateEntityFeature: 401>,
'target_temp_step': 0.5, 'target_temp_step': 0.5,
'temperature': 7, 'temperature': 7,
}), }),
@ -188,7 +188,7 @@
'original_name': 'Corridor', 'original_name': 'Corridor',
'platform': 'netatmo', 'platform': 'netatmo',
'previous_unique_id': None, 'previous_unique_id': None,
'supported_features': <ClimateEntityFeature: 17>, 'supported_features': <ClimateEntityFeature: 401>,
'translation_key': None, 'translation_key': None,
'unique_id': '1002003001-DeviceType.BNS', 'unique_id': '1002003001-DeviceType.BNS',
'unit_of_measurement': None, 'unit_of_measurement': None,
@ -215,7 +215,7 @@
'Schedule', 'Schedule',
]), ]),
'selected_schedule': 'Default', 'selected_schedule': 'Default',
'supported_features': <ClimateEntityFeature: 17>, 'supported_features': <ClimateEntityFeature: 401>,
'target_temp_step': 0.5, 'target_temp_step': 0.5,
'temperature': 22, 'temperature': 22,
}), }),
@ -265,7 +265,7 @@
'original_name': 'Entrada', 'original_name': 'Entrada',
'platform': 'netatmo', 'platform': 'netatmo',
'previous_unique_id': None, 'previous_unique_id': None,
'supported_features': <ClimateEntityFeature: 17>, 'supported_features': <ClimateEntityFeature: 401>,
'translation_key': None, 'translation_key': None,
'unique_id': '2833524037-DeviceType.NRV', 'unique_id': '2833524037-DeviceType.NRV',
'unit_of_measurement': None, 'unit_of_measurement': None,
@ -293,7 +293,7 @@
'Schedule', 'Schedule',
]), ]),
'selected_schedule': 'Default', 'selected_schedule': 'Default',
'supported_features': <ClimateEntityFeature: 17>, 'supported_features': <ClimateEntityFeature: 401>,
'target_temp_step': 0.5, 'target_temp_step': 0.5,
'temperature': 7, 'temperature': 7,
}), }),
@ -344,7 +344,7 @@
'original_name': 'Livingroom', 'original_name': 'Livingroom',
'platform': 'netatmo', 'platform': 'netatmo',
'previous_unique_id': None, 'previous_unique_id': None,
'supported_features': <ClimateEntityFeature: 17>, 'supported_features': <ClimateEntityFeature: 401>,
'translation_key': None, 'translation_key': None,
'unique_id': '2746182631-DeviceType.NATherm1', 'unique_id': '2746182631-DeviceType.NATherm1',
'unit_of_measurement': None, 'unit_of_measurement': None,
@ -372,7 +372,7 @@
'Schedule', 'Schedule',
]), ]),
'selected_schedule': 'Default', 'selected_schedule': 'Default',
'supported_features': <ClimateEntityFeature: 17>, 'supported_features': <ClimateEntityFeature: 401>,
'target_temp_step': 0.5, 'target_temp_step': 0.5,
'temperature': 12, 'temperature': 12,
}), }),

View file

@ -29,7 +29,7 @@ async def test_climate_zones(hass: HomeAssistant) -> None:
"min_temp": 12.8, "min_temp": 12.8,
"preset_mode": "None", "preset_mode": "None",
"preset_modes": ["None", "Home", "Away", "Sleep"], "preset_modes": ["None", "Home", "Away", "Sleep"],
"supported_features": 31, "supported_features": 415,
"target_temp_high": 26.1, "target_temp_high": 26.1,
"target_temp_low": 17.2, "target_temp_low": 17.2,
"target_temp_step": 1.0, "target_temp_step": 1.0,
@ -61,7 +61,7 @@ async def test_climate_zones(hass: HomeAssistant) -> None:
"min_temp": 12.8, "min_temp": 12.8,
"preset_mode": "None", "preset_mode": "None",
"preset_modes": ["None", "Home", "Away", "Sleep"], "preset_modes": ["None", "Home", "Away", "Sleep"],
"supported_features": 31, "supported_features": 415,
"target_temp_high": 26.1, "target_temp_high": 26.1,
"target_temp_low": 17.2, "target_temp_low": 17.2,
"target_temp_step": 1.0, "target_temp_step": 1.0,

View file

@ -12,7 +12,7 @@
]), ]),
'max_temp': 35.0, 'max_temp': 35.0,
'min_temp': 5.0, 'min_temp': 5.0,
'supported_features': <ClimateEntityFeature: 3>, 'supported_features': <ClimateEntityFeature: 259>,
'target_temp_high': 30.0, 'target_temp_high': 30.0,
'target_temp_low': 21.0, 'target_temp_low': 21.0,
'target_temp_step': 0.5, 'target_temp_step': 0.5,
@ -36,7 +36,7 @@
]), ]),
'max_temp': 35.0, 'max_temp': 35.0,
'min_temp': 5.0, 'min_temp': 5.0,
'supported_features': <ClimateEntityFeature: 3>, 'supported_features': <ClimateEntityFeature: 259>,
'target_temp_step': 0.5, 'target_temp_step': 0.5,
}), }),
'context': <ANY>, 'context': <ANY>,
@ -59,7 +59,7 @@
]), ]),
'max_temp': 35.0, 'max_temp': 35.0,
'min_temp': 5.0, 'min_temp': 5.0,
'supported_features': <ClimateEntityFeature: 3>, 'supported_features': <ClimateEntityFeature: 259>,
'target_temp_high': 30.0, 'target_temp_high': 30.0,
'target_temp_low': 21.0, 'target_temp_low': 21.0,
'target_temp_step': 0.5, 'target_temp_step': 0.5,
@ -83,7 +83,7 @@
]), ]),
'max_temp': 35.0, 'max_temp': 35.0,
'min_temp': 5.0, 'min_temp': 5.0,
'supported_features': <ClimateEntityFeature: 3>, 'supported_features': <ClimateEntityFeature: 259>,
'target_temp_step': 0.5, 'target_temp_step': 0.5,
}), }),
'context': <ANY>, 'context': <ANY>,
@ -112,7 +112,7 @@
]), ]),
'max_temp': 35.0, 'max_temp': 35.0,
'min_temp': 5.0, 'min_temp': 5.0,
'supported_features': <ClimateEntityFeature: 3>, 'supported_features': <ClimateEntityFeature: 259>,
'target_temp_high': 30.0, 'target_temp_high': 30.0,
'target_temp_low': 21.0, 'target_temp_low': 21.0,
'target_temp_step': 0.5, 'target_temp_step': 0.5,
@ -138,7 +138,7 @@
]), ]),
'max_temp': 35.0, 'max_temp': 35.0,
'min_temp': 5.0, 'min_temp': 5.0,
'supported_features': <ClimateEntityFeature: 3>, 'supported_features': <ClimateEntityFeature: 259>,
'target_temp_high': None, 'target_temp_high': None,
'target_temp_low': None, 'target_temp_low': None,
'target_temp_step': 0.5, 'target_temp_step': 0.5,
@ -164,7 +164,7 @@
]), ]),
'max_temp': 35.0, 'max_temp': 35.0,
'min_temp': 5.0, 'min_temp': 5.0,
'supported_features': <ClimateEntityFeature: 3>, 'supported_features': <ClimateEntityFeature: 259>,
'target_temp_high': None, 'target_temp_high': None,
'target_temp_low': None, 'target_temp_low': None,
'target_temp_step': 0.5, 'target_temp_step': 0.5,
@ -190,7 +190,7 @@
]), ]),
'max_temp': 35.0, 'max_temp': 35.0,
'min_temp': 5.0, 'min_temp': 5.0,
'supported_features': <ClimateEntityFeature: 3>, 'supported_features': <ClimateEntityFeature: 259>,
'target_temp_high': 30.0, 'target_temp_high': 30.0,
'target_temp_low': 21.0, 'target_temp_low': 21.0,
'target_temp_step': 0.5, 'target_temp_step': 0.5,
@ -216,7 +216,7 @@
]), ]),
'max_temp': 35.0, 'max_temp': 35.0,
'min_temp': 5.0, 'min_temp': 5.0,
'supported_features': <ClimateEntityFeature: 3>, 'supported_features': <ClimateEntityFeature: 259>,
'target_temp_high': 30.0, 'target_temp_high': 30.0,
'target_temp_low': 21.0, 'target_temp_low': 21.0,
'target_temp_step': 0.5, 'target_temp_step': 0.5,
@ -242,7 +242,7 @@
]), ]),
'max_temp': 35.0, 'max_temp': 35.0,
'min_temp': 5.0, 'min_temp': 5.0,
'supported_features': <ClimateEntityFeature: 3>, 'supported_features': <ClimateEntityFeature: 259>,
'target_temp_high': 30.0, 'target_temp_high': 30.0,
'target_temp_low': 21.0, 'target_temp_low': 21.0,
'target_temp_step': 0.5, 'target_temp_step': 0.5,
@ -268,7 +268,7 @@
]), ]),
'max_temp': 35.0, 'max_temp': 35.0,
'min_temp': 5.0, 'min_temp': 5.0,
'supported_features': <ClimateEntityFeature: 3>, 'supported_features': <ClimateEntityFeature: 259>,
'target_temp_high': None, 'target_temp_high': None,
'target_temp_low': None, 'target_temp_low': None,
'target_temp_step': 0.5, 'target_temp_step': 0.5,
@ -294,7 +294,7 @@
]), ]),
'max_temp': 35.0, 'max_temp': 35.0,
'min_temp': 5.0, 'min_temp': 5.0,
'supported_features': <ClimateEntityFeature: 3>, 'supported_features': <ClimateEntityFeature: 259>,
'target_temp_high': None, 'target_temp_high': None,
'target_temp_low': None, 'target_temp_low': None,
'target_temp_step': 0.5, 'target_temp_step': 0.5,
@ -320,7 +320,7 @@
]), ]),
'max_temp': 35.0, 'max_temp': 35.0,
'min_temp': 5.0, 'min_temp': 5.0,
'supported_features': <ClimateEntityFeature: 3>, 'supported_features': <ClimateEntityFeature: 259>,
'target_temp_high': 30.0, 'target_temp_high': 30.0,
'target_temp_low': 21.0, 'target_temp_low': 21.0,
'target_temp_step': 0.5, 'target_temp_step': 0.5,
@ -346,7 +346,7 @@
]), ]),
'max_temp': 35.0, 'max_temp': 35.0,
'min_temp': 5.0, 'min_temp': 5.0,
'supported_features': <ClimateEntityFeature: 3>, 'supported_features': <ClimateEntityFeature: 259>,
'target_temp_high': None, 'target_temp_high': None,
'target_temp_low': None, 'target_temp_low': None,
'target_temp_step': 0.5, 'target_temp_step': 0.5,
@ -372,7 +372,7 @@
]), ]),
'max_temp': 35.0, 'max_temp': 35.0,
'min_temp': 5.0, 'min_temp': 5.0,
'supported_features': <ClimateEntityFeature: 3>, 'supported_features': <ClimateEntityFeature: 259>,
'target_temp_high': None, 'target_temp_high': None,
'target_temp_low': None, 'target_temp_low': None,
'target_temp_step': 0.5, 'target_temp_step': 0.5,
@ -398,7 +398,7 @@
]), ]),
'max_temp': 35.0, 'max_temp': 35.0,
'min_temp': 5.0, 'min_temp': 5.0,
'supported_features': <ClimateEntityFeature: 3>, 'supported_features': <ClimateEntityFeature: 259>,
'target_temp_high': 30.0, 'target_temp_high': 30.0,
'target_temp_low': 21.0, 'target_temp_low': 21.0,
'target_temp_step': 0.5, 'target_temp_step': 0.5,
@ -424,7 +424,7 @@
]), ]),
'max_temp': 35.0, 'max_temp': 35.0,
'min_temp': 5.0, 'min_temp': 5.0,
'supported_features': <ClimateEntityFeature: 3>, 'supported_features': <ClimateEntityFeature: 259>,
'target_temp_high': 30.0, 'target_temp_high': 30.0,
'target_temp_low': 21.0, 'target_temp_low': 21.0,
'target_temp_step': 0.5, 'target_temp_step': 0.5,
@ -450,7 +450,7 @@
]), ]),
'max_temp': 35.0, 'max_temp': 35.0,
'min_temp': 5.0, 'min_temp': 5.0,
'supported_features': <ClimateEntityFeature: 3>, 'supported_features': <ClimateEntityFeature: 259>,
'target_temp_high': 30.0, 'target_temp_high': 30.0,
'target_temp_low': 21.0, 'target_temp_low': 21.0,
'target_temp_step': 0.5, 'target_temp_step': 0.5,
@ -476,7 +476,7 @@
]), ]),
'max_temp': 35.0, 'max_temp': 35.0,
'min_temp': 5.0, 'min_temp': 5.0,
'supported_features': <ClimateEntityFeature: 3>, 'supported_features': <ClimateEntityFeature: 259>,
'target_temp_high': None, 'target_temp_high': None,
'target_temp_low': None, 'target_temp_low': None,
'target_temp_step': 0.5, 'target_temp_step': 0.5,
@ -502,7 +502,7 @@
]), ]),
'max_temp': 35.0, 'max_temp': 35.0,
'min_temp': 5.0, 'min_temp': 5.0,
'supported_features': <ClimateEntityFeature: 3>, 'supported_features': <ClimateEntityFeature: 259>,
'target_temp_high': None, 'target_temp_high': None,
'target_temp_low': None, 'target_temp_low': None,
'target_temp_step': 0.5, 'target_temp_step': 0.5,

View file

@ -44,7 +44,7 @@ async def test_climate_thermostat_run(hass: HomeAssistant) -> None:
"min_temp": 5.0, "min_temp": 5.0,
"preset_mode": "Run Schedule", "preset_mode": "Run Schedule",
"preset_modes": ["Run Schedule", "Temporary Hold", "Permanent Hold"], "preset_modes": ["Run Schedule", "Temporary Hold", "Permanent Hold"],
"supported_features": 17, "supported_features": 273,
"temperature": 22.2, "temperature": 22.2,
} }
# Only test for a subset of attributes in case # Only test for a subset of attributes in case
@ -77,7 +77,7 @@ async def test_climate_thermostat_schedule_hold_unavailable(
"max_temp": 180.6, "max_temp": 180.6,
"min_temp": -6.1, "min_temp": -6.1,
"preset_modes": ["Run Schedule", "Temporary Hold", "Permanent Hold"], "preset_modes": ["Run Schedule", "Temporary Hold", "Permanent Hold"],
"supported_features": 17, "supported_features": 273,
} }
# Only test for a subset of attributes in case # Only test for a subset of attributes in case
# HA changes the implementation and a new one appears # HA changes the implementation and a new one appears
@ -110,7 +110,7 @@ async def test_climate_thermostat_schedule_hold_available(hass: HomeAssistant) -
"min_temp": -6.1, "min_temp": -6.1,
"preset_mode": "Run Schedule", "preset_mode": "Run Schedule",
"preset_modes": ["Run Schedule", "Temporary Hold", "Permanent Hold"], "preset_modes": ["Run Schedule", "Temporary Hold", "Permanent Hold"],
"supported_features": 17, "supported_features": 273,
"temperature": 26.1, "temperature": 26.1,
} }
# Only test for a subset of attributes in case # Only test for a subset of attributes in case
@ -144,7 +144,7 @@ async def test_climate_thermostat_schedule_temporary_hold(hass: HomeAssistant) -
"min_temp": -0.6, "min_temp": -0.6,
"preset_mode": "Run Schedule", "preset_mode": "Run Schedule",
"preset_modes": ["Run Schedule", "Temporary Hold", "Permanent Hold"], "preset_modes": ["Run Schedule", "Temporary Hold", "Permanent Hold"],
"supported_features": 17, "supported_features": 273,
"temperature": 37.2, "temperature": 37.2,
} }
# Only test for a subset of attributes in case # Only test for a subset of attributes in case

View file

@ -34,7 +34,7 @@ async def test_adam_climate_entity_attributes(
assert state.attributes["current_temperature"] == 20.9 assert state.attributes["current_temperature"] == 20.9
assert state.attributes["preset_mode"] == "home" assert state.attributes["preset_mode"] == "home"
assert state.attributes["supported_features"] == 17 assert state.attributes["supported_features"] == 273
assert state.attributes["temperature"] == 21.5 assert state.attributes["temperature"] == 21.5
assert state.attributes["min_temp"] == 0.0 assert state.attributes["min_temp"] == 0.0
assert state.attributes["max_temp"] == 35.0 assert state.attributes["max_temp"] == 35.0
@ -303,7 +303,7 @@ async def test_anna_climate_entity_attributes(
assert state.attributes["current_temperature"] == 19.3 assert state.attributes["current_temperature"] == 19.3
assert state.attributes["preset_mode"] == "home" assert state.attributes["preset_mode"] == "home"
assert state.attributes["supported_features"] == 18 assert state.attributes["supported_features"] == 274
assert state.attributes["target_temp_high"] == 30 assert state.attributes["target_temp_high"] == 30
assert state.attributes["target_temp_low"] == 20.5 assert state.attributes["target_temp_low"] == 20.5
assert state.attributes["min_temp"] == 4 assert state.attributes["min_temp"] == 4
@ -325,7 +325,7 @@ async def test_anna_2_climate_entity_attributes(
HVACMode.AUTO, HVACMode.AUTO,
HVACMode.HEAT_COOL, HVACMode.HEAT_COOL,
] ]
assert state.attributes["supported_features"] == 18 assert state.attributes["supported_features"] == 274
assert state.attributes["target_temp_high"] == 30 assert state.attributes["target_temp_high"] == 30
assert state.attributes["target_temp_low"] == 20.5 assert state.attributes["target_temp_low"] == 20.5

View file

@ -20,7 +20,7 @@
]), ]),
'max_temp': 20, 'max_temp': 20,
'min_temp': 10, 'min_temp': 10,
'supported_features': <ClimateEntityFeature: 41>, 'supported_features': <ClimateEntityFeature: 425>,
'swing_mode': 'stopped', 'swing_mode': 'stopped',
'swing_modes': list([ 'swing_modes': list([
'stopped', 'stopped',

View file

@ -320,6 +320,8 @@ async def test_air_conditioner_entity_state(
| ClimateEntityFeature.TARGET_TEMPERATURE | ClimateEntityFeature.TARGET_TEMPERATURE
| ClimateEntityFeature.PRESET_MODE | ClimateEntityFeature.PRESET_MODE
| ClimateEntityFeature.SWING_MODE | ClimateEntityFeature.SWING_MODE
| ClimateEntityFeature.TURN_OFF
| ClimateEntityFeature.TURN_ON
) )
assert sorted(state.attributes[ATTR_HVAC_MODES]) == [ assert sorted(state.attributes[ATTR_HVAC_MODES]) == [
HVACMode.COOL, HVACMode.COOL,

View file

@ -52,7 +52,9 @@ async def test_thermostat_update(
assert state.state == HVACMode.HEAT assert state.state == HVACMode.HEAT
assert ( assert (
state.attributes[ATTR_SUPPORTED_FEATURES] state.attributes[ATTR_SUPPORTED_FEATURES]
== ClimateEntityFeature.PRESET_MODE | ClimateEntityFeature.TARGET_TEMPERATURE == ClimateEntityFeature.PRESET_MODE
| ClimateEntityFeature.TARGET_TEMPERATURE
| ClimateEntityFeature.TURN_ON
) )
assert state.attributes[ATTR_CURRENT_TEMPERATURE] == 38 assert state.attributes[ATTR_CURRENT_TEMPERATURE] == 38
assert state.attributes[ATTR_TEMPERATURE] == 39 assert state.attributes[ATTR_TEMPERATURE] == 39

View file

@ -24,7 +24,7 @@ async def test_air_con(hass: HomeAssistant) -> None:
"min_temp": 16.0, "min_temp": 16.0,
"preset_mode": "auto", "preset_mode": "auto",
"preset_modes": ["away", "home", "auto"], "preset_modes": ["away", "home", "auto"],
"supported_features": 25, "supported_features": 409,
"target_temp_step": 1, "target_temp_step": 1,
"temperature": 17.8, "temperature": 17.8,
} }
@ -51,7 +51,7 @@ async def test_heater(hass: HomeAssistant) -> None:
"min_temp": 16.0, "min_temp": 16.0,
"preset_mode": "auto", "preset_mode": "auto",
"preset_modes": ["away", "home", "auto"], "preset_modes": ["away", "home", "auto"],
"supported_features": 17, "supported_features": 401,
"target_temp_step": 1, "target_temp_step": 1,
"temperature": 20.5, "temperature": 20.5,
} }
@ -81,7 +81,7 @@ async def test_smartac_with_swing(hass: HomeAssistant) -> None:
"preset_mode": "auto", "preset_mode": "auto",
"preset_modes": ["away", "home", "auto"], "preset_modes": ["away", "home", "auto"],
"swing_modes": ["on", "off"], "swing_modes": ["on", "off"],
"supported_features": 57, "supported_features": 441,
"target_temp_step": 1.0, "target_temp_step": 1.0,
"temperature": 20.0, "temperature": 20.0,
} }

View file

@ -10,6 +10,8 @@ EXPECTED_BASE_SUPPORTED_FEATURES = (
ClimateEntityFeature.TARGET_TEMPERATURE ClimateEntityFeature.TARGET_TEMPERATURE
| ClimateEntityFeature.FAN_MODE | ClimateEntityFeature.FAN_MODE
| ClimateEntityFeature.PRESET_MODE | ClimateEntityFeature.PRESET_MODE
| ClimateEntityFeature.TURN_OFF
| ClimateEntityFeature.TURN_ON
) )

View file

@ -97,6 +97,8 @@ async def test_static_attributes(
== ClimateEntityFeature.TARGET_TEMPERATURE == ClimateEntityFeature.TARGET_TEMPERATURE
| ClimateEntityFeature.FAN_MODE | ClimateEntityFeature.FAN_MODE
| ClimateEntityFeature.SWING_MODE | ClimateEntityFeature.SWING_MODE
| ClimateEntityFeature.TURN_OFF
| ClimateEntityFeature.TURN_ON
) )
assert attributes[ATTR_HVAC_MODES] == [ assert attributes[ATTR_HVAC_MODES] == [
HVACMode.COOL, HVACMode.COOL,

View file

@ -83,6 +83,8 @@ async def test_thermostat_v2(
== ClimateEntityFeature.TARGET_TEMPERATURE == ClimateEntityFeature.TARGET_TEMPERATURE
| ClimateEntityFeature.TARGET_TEMPERATURE_RANGE | ClimateEntityFeature.TARGET_TEMPERATURE_RANGE
| ClimateEntityFeature.FAN_MODE | ClimateEntityFeature.FAN_MODE
| ClimateEntityFeature.TURN_OFF
| ClimateEntityFeature.TURN_ON
) )
client.async_send_command.reset_mock() client.async_send_command.reset_mock()
@ -330,7 +332,7 @@ async def test_setpoint_thermostat(
assert state.attributes[ATTR_HVAC_MODES] == [HVACMode.HEAT] assert state.attributes[ATTR_HVAC_MODES] == [HVACMode.HEAT]
assert ( assert (
state.attributes[ATTR_SUPPORTED_FEATURES] state.attributes[ATTR_SUPPORTED_FEATURES]
== ClimateEntityFeature.TARGET_TEMPERATURE == ClimateEntityFeature.TARGET_TEMPERATURE | ClimateEntityFeature.TURN_ON
) )
client.async_send_command_no_wait.reset_mock() client.async_send_command_no_wait.reset_mock()
@ -432,7 +434,10 @@ async def test_thermostat_heatit_z_trm6(
assert state.attributes[ATTR_HVAC_ACTION] == HVACAction.IDLE assert state.attributes[ATTR_HVAC_ACTION] == HVACAction.IDLE
assert ( assert (
state.attributes[ATTR_SUPPORTED_FEATURES] state.attributes[ATTR_SUPPORTED_FEATURES]
== ClimateEntityFeature.TARGET_TEMPERATURE | ClimateEntityFeature.PRESET_MODE == ClimateEntityFeature.TARGET_TEMPERATURE
| ClimateEntityFeature.PRESET_MODE
| ClimateEntityFeature.TURN_OFF
| ClimateEntityFeature.TURN_ON
) )
assert state.attributes[ATTR_MIN_TEMP] == 5 assert state.attributes[ATTR_MIN_TEMP] == 5
assert state.attributes[ATTR_MAX_TEMP] == 40 assert state.attributes[ATTR_MAX_TEMP] == 40
@ -513,6 +518,8 @@ async def test_thermostat_heatit_z_trm3(
assert ( assert (
state.attributes[ATTR_SUPPORTED_FEATURES] state.attributes[ATTR_SUPPORTED_FEATURES]
== ClimateEntityFeature.TARGET_TEMPERATURE == ClimateEntityFeature.TARGET_TEMPERATURE
| ClimateEntityFeature.TURN_OFF
| ClimateEntityFeature.TURN_ON
) )
assert state.attributes[ATTR_MIN_TEMP] == 5 assert state.attributes[ATTR_MIN_TEMP] == 5
assert state.attributes[ATTR_MAX_TEMP] == 35 assert state.attributes[ATTR_MAX_TEMP] == 35
@ -582,7 +589,10 @@ async def test_thermostat_heatit_z_trm2fx(
assert state.attributes[ATTR_TEMPERATURE] == 29 assert state.attributes[ATTR_TEMPERATURE] == 29
assert ( assert (
state.attributes[ATTR_SUPPORTED_FEATURES] state.attributes[ATTR_SUPPORTED_FEATURES]
== ClimateEntityFeature.TARGET_TEMPERATURE | ClimateEntityFeature.PRESET_MODE == ClimateEntityFeature.TARGET_TEMPERATURE
| ClimateEntityFeature.PRESET_MODE
| ClimateEntityFeature.TURN_OFF
| ClimateEntityFeature.TURN_ON
) )
assert state.attributes[ATTR_MIN_TEMP] == 7 assert state.attributes[ATTR_MIN_TEMP] == 7
assert state.attributes[ATTR_MAX_TEMP] == 35 assert state.attributes[ATTR_MAX_TEMP] == 35
@ -627,7 +637,7 @@ async def test_thermostat_srt321_hrt4_zw(
HVACMode.HEAT, HVACMode.HEAT,
] ]
assert state.attributes[ATTR_CURRENT_TEMPERATURE] is None assert state.attributes[ATTR_CURRENT_TEMPERATURE] is None
assert state.attributes[ATTR_SUPPORTED_FEATURES] == 0 assert state.attributes[ATTR_SUPPORTED_FEATURES] == 384
async def test_preset_and_no_setpoint( async def test_preset_and_no_setpoint(