core/tests/components/climate/test_init.py

1291 lines
39 KiB
Python
Raw Normal View History

"""The tests for the climate component."""
2021-03-18 14:13:22 +00:00
from __future__ import annotations
from enum import Enum
from types import ModuleType
from typing import Any
from unittest.mock import MagicMock, Mock, patch
import pytest
import voluptuous as vol
from homeassistant.components import climate
from homeassistant.components.climate import (
DOMAIN,
SET_TEMPERATURE_SCHEMA,
ClimateEntity,
HVACMode,
)
from homeassistant.components.climate.const import (
ATTR_CURRENT_TEMPERATURE,
ATTR_FAN_MODE,
ATTR_MAX_TEMP,
ATTR_MIN_TEMP,
ATTR_PRESET_MODE,
ATTR_SWING_MODE,
ATTR_TARGET_TEMP_HIGH,
ATTR_TARGET_TEMP_LOW,
SERVICE_SET_FAN_MODE,
SERVICE_SET_PRESET_MODE,
SERVICE_SET_SWING_MODE,
SERVICE_SET_TEMPERATURE,
ClimateEntityFeature,
)
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import (
ATTR_TEMPERATURE,
PRECISION_WHOLE,
SERVICE_TURN_OFF,
SERVICE_TURN_ON,
UnitOfTemperature,
)
from homeassistant.core import HomeAssistant
from homeassistant.exceptions import ServiceValidationError
from homeassistant.helpers import issue_registry as ir
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from tests.common import (
MockConfigEntry,
MockEntity,
MockModule,
MockPlatform,
async_mock_service,
help_test_all,
import_and_test_deprecated_constant,
import_and_test_deprecated_constant_enum,
mock_integration,
mock_platform,
)
async def test_set_temp_schema_no_req(
hass: HomeAssistant, caplog: pytest.LogCaptureFixture
) -> None:
"""Test the set temperature schema with missing required data."""
2019-07-31 19:25:30 +00:00
domain = "climate"
service = "test_set_temperature"
schema = SET_TEMPERATURE_SCHEMA
calls = async_mock_service(hass, domain, service, schema)
2019-07-31 19:25:30 +00:00
data = {"hvac_mode": "off", "entity_id": ["climate.test_id"]}
with pytest.raises(vol.Invalid):
Climate 1.0 (#23899) * Climate 1.0 / part 1/2/3 * fix flake * Lint * Update Google Assistant * ambiclimate to climate 1.0 (#24911) * Fix Alexa * Lint * Migrate zhong_hong * Migrate tuya * Migrate honeywell to new climate schema (#24257) * Update one * Fix model climate v2 * Cleanup p4 * Add comfort hold mode * Fix old code * Update homeassistant/components/climate/__init__.py Co-Authored-By: Paulus Schoutsen <paulus@home-assistant.io> * Update homeassistant/components/climate/const.py Co-Authored-By: Paulus Schoutsen <paulus@home-assistant.io> * First renaming * Rename operation to hvac for paulus * Rename hold mode to preset mode * Cleanup & update comments * Remove on/off * Fix supported feature count * Update services * Update demo * Fix tests & use current_hvac * Update comment * Fix tests & add typing * Add more typing * Update modes * Fix tests * Cleanup low/high with range * Update homematic part 1 * Finish homematic * Fix lint * fix hm mapping * Support simple devices * convert lcn * migrate oem * Fix xs1 * update hive * update mil * Update toon * migrate deconz * cleanup * update tesla * Fix lint * Fix vera * Migrate zwave * Migrate velbus * Cleanup humity feature * Cleanup * Migrate wink * migrate dyson * Fix current hvac * Renaming * Fix lint * Migrate tfiac * migrate tado * Fix PRESET can be None * apply PR#23913 from dev * remove EU component, etc. * remove EU component, etc. * ready to test now * de-linted * some tweaks * de-lint * better handling of edge cases * delint * fix set_mode typos * apply PR#23913 from dev * remove EU component, etc. * ready to test now * de-linted * some tweaks * de-lint * better handling of edge cases * delint * fix set_mode typos * delint, move debug code * away preset now working * code tidy-up * code tidy-up 2 * code tidy-up 3 * address issues #18932, #15063 * address issues #18932, #15063 - 2/2 * refactor MODE_AUTO to MODE_HEAT_COOL and use F not C * add low/high to set_temp * add low/high to set_temp 2 * add low/high to set_temp - delint * run HA scripts * port changes from PR #24402 * manual rebase * manual rebase 2 * delint * minor change * remove SUPPORT_HVAC_ACTION * Migrate radiotherm * Convert touchline * Migrate flexit * Migrate nuheat * Migrate maxcube * Fix names maxcube const * Migrate proliphix * Migrate heatmiser * Migrate fritzbox * Migrate opentherm_gw * Migrate venstar * Migrate daikin * Migrate modbus * Fix elif * Migrate Homematic IP Cloud to climate-1.0 (#24913) * hmip climate fix * Update hvac_mode and preset_mode * fix lint * Fix lint * Migrate generic_thermostat * Migrate incomfort to new climate schema (#24915) * initial commit * Update climate.py * Migrate eq3btsmart * Lint * cleanup PRESET_MANUAL * Migrate ecobee * No conditional features * KNX: Migrate climate component to new climate platform (#24931) * Migrate climate component * Remove unused code * Corrected line length * Lint * Lint * fix tests * Fix value * Migrate geniushub to new climate schema (#24191) * Update one * Fix model climate v2 * Cleanup p4 * Add comfort hold mode * Fix old code * Update homeassistant/components/climate/__init__.py Co-Authored-By: Paulus Schoutsen <paulus@home-assistant.io> * Update homeassistant/components/climate/const.py Co-Authored-By: Paulus Schoutsen <paulus@home-assistant.io> * First renaming * Rename operation to hvac for paulus * Rename hold mode to preset mode * Cleanup & update comments * Remove on/off * Fix supported feature count * Update services * Update demo * Fix tests & use current_hvac * Update comment * Fix tests & add typing * Add more typing * Update modes * Fix tests * Cleanup low/high with range * Update homematic part 1 * Finish homematic * Fix lint * fix hm mapping * Support simple devices * convert lcn * migrate oem * Fix xs1 * update hive * update mil * Update toon * migrate deconz * cleanup * update tesla * Fix lint * Fix vera * Migrate zwave * Migrate velbus * Cleanup humity feature * Cleanup * Migrate wink * migrate dyson * Fix current hvac * Renaming * Fix lint * Migrate tfiac * migrate tado * delinted * delinted * use latest client * clean up mappings * clean up mappings * add duration to set_temperature * add duration to set_temperature * manual rebase * tweak * fix regression * small fix * fix rebase mixup * address comments * finish refactor * fix regression * tweak type hints * delint * manual rebase * WIP: Fixes for honeywell migration to climate-1.0 (#24938) * add type hints * code tidy-up * Fixes for incomfort migration to climate-1.0 (#24936) * delint type hints * no async unless await * revert: no async unless await * revert: no async unless await 2 * delint * fix typo * Fix homekit_controller on climate-1.0 (#24948) * Fix tests on climate-1.0 branch * As part of climate-1.0, make state return the heating-cooling.current characteristic * Fixes from review * lint * Fix imports * Migrate stibel_eltron * Fix lint * Migrate coolmaster to climate 1.0 (#24967) * Migrate coolmaster to climate 1.0 * fix lint errors * More lint fixes * Fix demo to work with UI * Migrate spider * Demo update * Updated frontend to 20190705.0 * Fix boost mode (#24980) * Prepare Netatmo for climate 1.0 (#24973) * Migration Netatmo * Address comments * Update climate.py * Migrate ephember * Migrate Sensibo * Implemented review comments (#24942) * Migrate ESPHome * Migrate MQTT * Migrate Nest * Migrate melissa * Initial/partial migration of ST * Migrate ST * Remove Away mode (#24995) * Migrate evohome, cache access tokens (#24491) * add water_heater, add storage - initial commit * add water_heater, add storage - initial commit delint add missing code desiderata update honeywell client library & CODEOWNER add auth_tokens code, refactor & delint refactor for broker delint * Add Broker, Water Heater & Refactor add missing code desiderata * update honeywell client library & CODEOWNER add auth_tokens code, refactor & delint refactor for broker * bugfix - loc_idx may not be 0 more refactor - ensure pure async more refactoring appears all r/o attributes are working tweak precsion, DHW & delint remove unused code remove unused code 2 remove unused code, refactor _save_auth_tokens() * support RoundThermostat bugfix opmode, switch to util.dt, add until=1h revert breaking change * store at_expires as naive UTC remove debug code delint tidy up exception handling delint add water_heater, add storage - initial commit delint add missing code desiderata update honeywell client library & CODEOWNER add auth_tokens code, refactor & delint refactor for broker add water_heater, add storage - initial commit delint add missing code desiderata update honeywell client library & CODEOWNER add auth_tokens code, refactor & delint refactor for broker delint bugfix - loc_idx may not be 0 more refactor - ensure pure async more refactoring appears all r/o attributes are working tweak precsion, DHW & delint remove unused code remove unused code 2 remove unused code, refactor _save_auth_tokens() support RoundThermostat bugfix opmode, switch to util.dt, add until=1h revert breaking change store at_expires as naive UTC remove debug code delint tidy up exception handling delint * update CODEOWNERS * fix regression * fix requirements * migrate to climate-1.0 * tweaking * de-lint * TCS working? & delint * tweaking * TCS code finalised * remove available() logic * refactor _switchpoints() * tidy up switchpoint code * tweak * teaking device_state_attributes * some refactoring * move PRESET_CUSTOM back to evohome * move CONF_ACCESS_TOKEN_EXPIRES CONF_REFRESH_TOKEN back to evohome * refactor SP code and dt conversion * delinted * delinted * remove water_heater * fix regression * Migrate homekit * Cleanup away mode * Fix tests * add helpers * fix tests melissa * Fix nehueat * fix zwave * add more tests * fix deconz * Fix climate test emulate_hue * fix tests * fix dyson tests * fix demo with new layout * fix honeywell * Switch homekit_controller to use HVAC_MODE_HEAT_COOL instead of HVAC_MODE_AUTO (#25009) * Lint * PyLint * Pylint * fix fritzbox tests * Fix google * Fix all tests * Fix lint * Fix auto for homekit like controler * Fix lint * fix lint
2019-07-08 12:00:24 +00:00
await hass.services.async_call(domain, service, data)
await hass.async_block_till_done()
assert len(calls) == 0
async def test_set_temp_schema(
hass: HomeAssistant, caplog: pytest.LogCaptureFixture
) -> None:
"""Test the set temperature schema with ok required data."""
2019-07-31 19:25:30 +00:00
domain = "climate"
service = "test_set_temperature"
schema = SET_TEMPERATURE_SCHEMA
calls = async_mock_service(hass, domain, service, schema)
2019-07-31 19:25:30 +00:00
data = {"temperature": 20.0, "hvac_mode": "heat", "entity_id": ["climate.test_id"]}
Climate 1.0 (#23899) * Climate 1.0 / part 1/2/3 * fix flake * Lint * Update Google Assistant * ambiclimate to climate 1.0 (#24911) * Fix Alexa * Lint * Migrate zhong_hong * Migrate tuya * Migrate honeywell to new climate schema (#24257) * Update one * Fix model climate v2 * Cleanup p4 * Add comfort hold mode * Fix old code * Update homeassistant/components/climate/__init__.py Co-Authored-By: Paulus Schoutsen <paulus@home-assistant.io> * Update homeassistant/components/climate/const.py Co-Authored-By: Paulus Schoutsen <paulus@home-assistant.io> * First renaming * Rename operation to hvac for paulus * Rename hold mode to preset mode * Cleanup & update comments * Remove on/off * Fix supported feature count * Update services * Update demo * Fix tests & use current_hvac * Update comment * Fix tests & add typing * Add more typing * Update modes * Fix tests * Cleanup low/high with range * Update homematic part 1 * Finish homematic * Fix lint * fix hm mapping * Support simple devices * convert lcn * migrate oem * Fix xs1 * update hive * update mil * Update toon * migrate deconz * cleanup * update tesla * Fix lint * Fix vera * Migrate zwave * Migrate velbus * Cleanup humity feature * Cleanup * Migrate wink * migrate dyson * Fix current hvac * Renaming * Fix lint * Migrate tfiac * migrate tado * Fix PRESET can be None * apply PR#23913 from dev * remove EU component, etc. * remove EU component, etc. * ready to test now * de-linted * some tweaks * de-lint * better handling of edge cases * delint * fix set_mode typos * apply PR#23913 from dev * remove EU component, etc. * ready to test now * de-linted * some tweaks * de-lint * better handling of edge cases * delint * fix set_mode typos * delint, move debug code * away preset now working * code tidy-up * code tidy-up 2 * code tidy-up 3 * address issues #18932, #15063 * address issues #18932, #15063 - 2/2 * refactor MODE_AUTO to MODE_HEAT_COOL and use F not C * add low/high to set_temp * add low/high to set_temp 2 * add low/high to set_temp - delint * run HA scripts * port changes from PR #24402 * manual rebase * manual rebase 2 * delint * minor change * remove SUPPORT_HVAC_ACTION * Migrate radiotherm * Convert touchline * Migrate flexit * Migrate nuheat * Migrate maxcube * Fix names maxcube const * Migrate proliphix * Migrate heatmiser * Migrate fritzbox * Migrate opentherm_gw * Migrate venstar * Migrate daikin * Migrate modbus * Fix elif * Migrate Homematic IP Cloud to climate-1.0 (#24913) * hmip climate fix * Update hvac_mode and preset_mode * fix lint * Fix lint * Migrate generic_thermostat * Migrate incomfort to new climate schema (#24915) * initial commit * Update climate.py * Migrate eq3btsmart * Lint * cleanup PRESET_MANUAL * Migrate ecobee * No conditional features * KNX: Migrate climate component to new climate platform (#24931) * Migrate climate component * Remove unused code * Corrected line length * Lint * Lint * fix tests * Fix value * Migrate geniushub to new climate schema (#24191) * Update one * Fix model climate v2 * Cleanup p4 * Add comfort hold mode * Fix old code * Update homeassistant/components/climate/__init__.py Co-Authored-By: Paulus Schoutsen <paulus@home-assistant.io> * Update homeassistant/components/climate/const.py Co-Authored-By: Paulus Schoutsen <paulus@home-assistant.io> * First renaming * Rename operation to hvac for paulus * Rename hold mode to preset mode * Cleanup & update comments * Remove on/off * Fix supported feature count * Update services * Update demo * Fix tests & use current_hvac * Update comment * Fix tests & add typing * Add more typing * Update modes * Fix tests * Cleanup low/high with range * Update homematic part 1 * Finish homematic * Fix lint * fix hm mapping * Support simple devices * convert lcn * migrate oem * Fix xs1 * update hive * update mil * Update toon * migrate deconz * cleanup * update tesla * Fix lint * Fix vera * Migrate zwave * Migrate velbus * Cleanup humity feature * Cleanup * Migrate wink * migrate dyson * Fix current hvac * Renaming * Fix lint * Migrate tfiac * migrate tado * delinted * delinted * use latest client * clean up mappings * clean up mappings * add duration to set_temperature * add duration to set_temperature * manual rebase * tweak * fix regression * small fix * fix rebase mixup * address comments * finish refactor * fix regression * tweak type hints * delint * manual rebase * WIP: Fixes for honeywell migration to climate-1.0 (#24938) * add type hints * code tidy-up * Fixes for incomfort migration to climate-1.0 (#24936) * delint type hints * no async unless await * revert: no async unless await * revert: no async unless await 2 * delint * fix typo * Fix homekit_controller on climate-1.0 (#24948) * Fix tests on climate-1.0 branch * As part of climate-1.0, make state return the heating-cooling.current characteristic * Fixes from review * lint * Fix imports * Migrate stibel_eltron * Fix lint * Migrate coolmaster to climate 1.0 (#24967) * Migrate coolmaster to climate 1.0 * fix lint errors * More lint fixes * Fix demo to work with UI * Migrate spider * Demo update * Updated frontend to 20190705.0 * Fix boost mode (#24980) * Prepare Netatmo for climate 1.0 (#24973) * Migration Netatmo * Address comments * Update climate.py * Migrate ephember * Migrate Sensibo * Implemented review comments (#24942) * Migrate ESPHome * Migrate MQTT * Migrate Nest * Migrate melissa * Initial/partial migration of ST * Migrate ST * Remove Away mode (#24995) * Migrate evohome, cache access tokens (#24491) * add water_heater, add storage - initial commit * add water_heater, add storage - initial commit delint add missing code desiderata update honeywell client library & CODEOWNER add auth_tokens code, refactor & delint refactor for broker delint * Add Broker, Water Heater & Refactor add missing code desiderata * update honeywell client library & CODEOWNER add auth_tokens code, refactor & delint refactor for broker * bugfix - loc_idx may not be 0 more refactor - ensure pure async more refactoring appears all r/o attributes are working tweak precsion, DHW & delint remove unused code remove unused code 2 remove unused code, refactor _save_auth_tokens() * support RoundThermostat bugfix opmode, switch to util.dt, add until=1h revert breaking change * store at_expires as naive UTC remove debug code delint tidy up exception handling delint add water_heater, add storage - initial commit delint add missing code desiderata update honeywell client library & CODEOWNER add auth_tokens code, refactor & delint refactor for broker add water_heater, add storage - initial commit delint add missing code desiderata update honeywell client library & CODEOWNER add auth_tokens code, refactor & delint refactor for broker delint bugfix - loc_idx may not be 0 more refactor - ensure pure async more refactoring appears all r/o attributes are working tweak precsion, DHW & delint remove unused code remove unused code 2 remove unused code, refactor _save_auth_tokens() support RoundThermostat bugfix opmode, switch to util.dt, add until=1h revert breaking change store at_expires as naive UTC remove debug code delint tidy up exception handling delint * update CODEOWNERS * fix regression * fix requirements * migrate to climate-1.0 * tweaking * de-lint * TCS working? & delint * tweaking * TCS code finalised * remove available() logic * refactor _switchpoints() * tidy up switchpoint code * tweak * teaking device_state_attributes * some refactoring * move PRESET_CUSTOM back to evohome * move CONF_ACCESS_TOKEN_EXPIRES CONF_REFRESH_TOKEN back to evohome * refactor SP code and dt conversion * delinted * delinted * remove water_heater * fix regression * Migrate homekit * Cleanup away mode * Fix tests * add helpers * fix tests melissa * Fix nehueat * fix zwave * add more tests * fix deconz * Fix climate test emulate_hue * fix tests * fix dyson tests * fix demo with new layout * fix honeywell * Switch homekit_controller to use HVAC_MODE_HEAT_COOL instead of HVAC_MODE_AUTO (#25009) * Lint * PyLint * Pylint * fix fritzbox tests * Fix google * Fix all tests * Fix lint * Fix auto for homekit like controler * Fix lint * fix lint
2019-07-08 12:00:24 +00:00
await hass.services.async_call(domain, service, data)
await hass.async_block_till_done()
assert len(calls) == 1
assert calls[-1].data == data
class MockClimateEntity(MockEntity, ClimateEntity):
"""Mock Climate device to use in tests."""
_attr_supported_features = (
ClimateEntityFeature.FAN_MODE
| ClimateEntityFeature.PRESET_MODE
| ClimateEntityFeature.SWING_MODE
)
_attr_preset_mode = "home"
_attr_preset_modes = ["home", "away"]
_attr_fan_mode = "auto"
_attr_fan_modes = ["auto", "off"]
_attr_swing_mode = "auto"
_attr_swing_modes = ["auto", "off"]
_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]
def set_preset_mode(self, preset_mode: str) -> None:
"""Set preset mode."""
self._attr_preset_mode = preset_mode
def set_fan_mode(self, fan_mode: str) -> None:
"""Set fan mode."""
self._attr_fan_mode = fan_mode
def set_swing_mode(self, swing_mode: str) -> None:
"""Set swing mode."""
self._attr_swing_mode = swing_mode
class MockClimateEntityTestMethods(MockClimateEntity):
"""Mock Climate device."""
def turn_on(self) -> None:
"""Turn on."""
def turn_off(self) -> None:
"""Turn off."""
async def test_sync_turn_on(hass: HomeAssistant) -> None:
"""Test if async turn_on calls sync turn_on."""
climate = MockClimateEntityTestMethods()
climate.hass = hass
climate.turn_on = MagicMock()
await climate.async_turn_on()
assert climate.turn_on.called
async def test_sync_turn_off(hass: HomeAssistant) -> None:
"""Test if async turn_off calls sync turn_off."""
climate = MockClimateEntityTestMethods()
climate.hass = hass
climate.turn_off = MagicMock()
await climate.async_turn_off()
assert climate.turn_off.called
def _create_tuples(enum: type[Enum], constant_prefix: str) -> list[tuple[Enum, str]]:
return [
(enum_field, constant_prefix)
for enum_field in enum
if enum_field
not in [ClimateEntityFeature.TURN_ON, ClimateEntityFeature.TURN_OFF]
]
@pytest.mark.parametrize(
"module",
[climate, climate.const],
)
def test_all(module: ModuleType) -> None:
"""Test module.__all__ is correctly set."""
help_test_all(module)
@pytest.mark.parametrize(
("enum", "constant_prefix"),
_create_tuples(climate.ClimateEntityFeature, "SUPPORT_")
+ _create_tuples(climate.HVACMode, "HVAC_MODE_"),
)
@pytest.mark.parametrize(
"module",
[climate, climate.const],
)
def test_deprecated_constants(
caplog: pytest.LogCaptureFixture,
enum: Enum,
constant_prefix: str,
module: ModuleType,
) -> None:
"""Test deprecated constants."""
import_and_test_deprecated_constant_enum(
caplog, module, enum, constant_prefix, "2025.1"
)
@pytest.mark.parametrize(
("enum", "constant_postfix"),
[
(climate.HVACAction.OFF, "OFF"),
(climate.HVACAction.HEATING, "HEAT"),
(climate.HVACAction.COOLING, "COOL"),
(climate.HVACAction.DRYING, "DRY"),
(climate.HVACAction.IDLE, "IDLE"),
(climate.HVACAction.FAN, "FAN"),
],
)
def test_deprecated_current_constants(
caplog: pytest.LogCaptureFixture,
enum: climate.HVACAction,
constant_postfix: str,
) -> None:
"""Test deprecated current constants."""
import_and_test_deprecated_constant(
caplog,
climate.const,
"CURRENT_HVAC_" + constant_postfix,
f"{enum.__class__.__name__}.{enum.name}",
enum,
"2025.1",
)
async def test_preset_mode_validation(
hass: HomeAssistant, config_flow_fixture: None
) -> None:
"""Test mode validation for fan, swing and preset."""
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([MockClimateEntity(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.attributes.get(ATTR_PRESET_MODE) == "home"
assert state.attributes.get(ATTR_FAN_MODE) == "auto"
assert state.attributes.get(ATTR_SWING_MODE) == "auto"
await hass.services.async_call(
DOMAIN,
SERVICE_SET_PRESET_MODE,
{
"entity_id": "climate.test",
"preset_mode": "away",
},
blocking=True,
)
await hass.services.async_call(
DOMAIN,
SERVICE_SET_SWING_MODE,
{
"entity_id": "climate.test",
"swing_mode": "off",
},
blocking=True,
)
await hass.services.async_call(
DOMAIN,
SERVICE_SET_FAN_MODE,
{
"entity_id": "climate.test",
"fan_mode": "off",
},
blocking=True,
)
state = hass.states.get("climate.test")
assert state.attributes.get(ATTR_PRESET_MODE) == "away"
assert state.attributes.get(ATTR_FAN_MODE) == "off"
assert state.attributes.get(ATTR_SWING_MODE) == "off"
with pytest.raises(
ServiceValidationError,
match="Preset mode invalid is not valid. Valid preset modes are: home, away",
) as exc:
await hass.services.async_call(
DOMAIN,
SERVICE_SET_PRESET_MODE,
{
"entity_id": "climate.test",
"preset_mode": "invalid",
},
blocking=True,
)
assert (
str(exc.value)
== "Preset mode invalid is not valid. Valid preset modes are: home, away"
)
assert exc.value.translation_key == "not_valid_preset_mode"
with pytest.raises(
ServiceValidationError,
match="Swing mode invalid is not valid. Valid swing modes are: auto, off",
) as exc:
await hass.services.async_call(
DOMAIN,
SERVICE_SET_SWING_MODE,
{
"entity_id": "climate.test",
"swing_mode": "invalid",
},
blocking=True,
)
assert (
str(exc.value)
== "Swing mode invalid is not valid. Valid swing modes are: auto, off"
)
assert exc.value.translation_key == "not_valid_swing_mode"
with pytest.raises(
ServiceValidationError,
match="Fan mode invalid is not valid. Valid fan modes are: auto, off",
) as exc:
await hass.services.async_call(
DOMAIN,
SERVICE_SET_FAN_MODE,
{
"entity_id": "climate.test",
"fan_mode": "invalid",
},
blocking=True,
)
assert (
str(exc.value)
== "Fan mode invalid is not valid. Valid fan modes are: auto, off"
)
assert exc.value.translation_key == "not_valid_fan_mode"
@pytest.mark.parametrize(
"supported_features_at_int",
[
ClimateEntityFeature.TARGET_TEMPERATURE.value,
ClimateEntityFeature.TARGET_TEMPERATURE.value
| ClimateEntityFeature.TURN_ON.value
| ClimateEntityFeature.TURN_OFF.value,
],
)
def test_deprecated_supported_features_ints(
caplog: pytest.LogCaptureFixture, supported_features_at_int: int
) -> None:
"""Test deprecated supported features ints."""
class MockClimateEntity(ClimateEntity):
@property
def supported_features(self) -> int:
"""Return supported features."""
return supported_features_at_int
entity = MockClimateEntity()
assert entity.supported_features is ClimateEntityFeature(supported_features_at_int)
assert "MockClimateEntity" in caplog.text
assert "is using deprecated supported features values" in caplog.text
assert "Instead it should use" in caplog.text
assert "ClimateEntityFeature.TARGET_TEMPERATURE" in caplog.text
caplog.clear()
assert entity.supported_features is ClimateEntityFeature(supported_features_at_int)
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),
)
with patch.object(
MockClimateEntityTest, "__module__", "tests.custom_components.climate.test_init"
):
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.custom_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.custom_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),
)
with patch.object(
MockClimateEntityTest, "__module__", "tests.custom_components.climate.test_init"
):
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.custom_components.climate.test_init."
"test_implicit_warning_not_implemented_turn_on_off_feature.<locals>.MockClimateEntityTest'>)"
" implements HVACMode(s): off, heat and therefore implicitly supports the turn_on/turn_off"
" methods 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),
)
with patch.object(
MockClimateEntityTest, "__module__", "tests.custom_components.climate.test_init"
):
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(s): off, heat and therefore implicitly supports the off, heat methods"
not in caplog.text
)
async def test_no_warning_integration_has_migrated(
hass: HomeAssistant, caplog: pytest.LogCaptureFixture, config_flow_fixture: None
) -> None:
"""Test no warning when integration migrated using `_enable_turn_on_off_backwards_compatibility`."""
class MockClimateEntityTest(MockClimateEntity):
"""Mock Climate device."""
_enable_turn_on_off_backwards_compatibility = False
_attr_supported_features = (
ClimateEntityFeature.FAN_MODE
| ClimateEntityFeature.PRESET_MODE
| ClimateEntityFeature.SWING_MODE
)
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),
)
with patch.object(
MockClimateEntityTest, "__module__", "tests.custom_components.climate.test_init"
):
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(s): off, heat and therefore implicitly supports the off, heat methods"
not in caplog.text
)
async def test_no_warning_integration_implement_feature_flags(
hass: HomeAssistant, caplog: pytest.LogCaptureFixture, config_flow_fixture: None
) -> None:
"""Test no warning when integration uses the correct feature flags."""
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),
)
with patch.object(
MockClimateEntityTest, "__module__", "tests.custom_components.climate.test_init"
):
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" not in caplog.text
assert "implements HVACMode(s):" not in caplog.text
async def test_turn_on_off_toggle(hass: HomeAssistant) -> None:
"""Test turn_on/turn_off/toggle methods."""
class MockClimateEntityTest(MockClimateEntity):
"""Mock Climate device."""
_attr_hvac_mode = HVACMode.OFF
@property
def hvac_mode(self) -> HVACMode:
"""Return hvac mode."""
return self._attr_hvac_mode
async def async_set_hvac_mode(self, hvac_mode: HVACMode) -> None:
"""Set new target hvac mode."""
self._attr_hvac_mode = hvac_mode
climate = MockClimateEntityTest()
climate.hass = hass
await climate.async_turn_on()
assert climate.hvac_mode == HVACMode.HEAT
await climate.async_turn_off()
assert climate.hvac_mode == HVACMode.OFF
await climate.async_toggle()
assert climate.hvac_mode == HVACMode.HEAT
await climate.async_toggle()
assert climate.hvac_mode == HVACMode.OFF
async def test_sync_toggle(hass: HomeAssistant) -> None:
"""Test if async toggle calls sync toggle."""
class MockClimateEntityTest(MockClimateEntity):
"""Mock Climate device."""
_enable_turn_on_off_backwards_compatibility = False
_attr_supported_features = (
ClimateEntityFeature.TURN_OFF | ClimateEntityFeature.TURN_ON
)
@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]
def turn_on(self) -> None:
"""Turn on."""
def turn_off(self) -> None:
"""Turn off."""
def toggle(self) -> None:
"""Toggle."""
climate = MockClimateEntityTest()
climate.hass = hass
climate.toggle = Mock()
await climate.async_toggle()
assert climate.toggle.called
ISSUE_TRACKER = "https://blablabla.com"
@pytest.mark.parametrize(
(
"manifest_extra",
"translation_key",
"translation_placeholders_extra",
"report",
"module",
),
[
(
{},
"deprecated_climate_aux_no_url",
{},
"report it to the author of the 'test' custom integration",
"custom_components.test.climate",
),
(
{"issue_tracker": ISSUE_TRACKER},
"deprecated_climate_aux_url_custom",
{"issue_tracker": ISSUE_TRACKER},
"create a bug report at https://blablabla.com",
"custom_components.test.climate",
),
],
)
async def test_issue_aux_property_deprecated(
hass: HomeAssistant,
caplog: pytest.LogCaptureFixture,
config_flow_fixture: None,
manifest_extra: dict[str, str],
translation_key: str,
translation_placeholders_extra: dict[str, str],
report: str,
module: str,
issue_registry: ir.IssueRegistry,
) -> None:
"""Test the issue is raised on deprecated auxiliary heater attributes."""
class MockClimateEntityWithAux(MockClimateEntity):
"""Mock climate class with mocked aux heater."""
_attr_supported_features = (
ClimateEntityFeature.AUX_HEAT | ClimateEntityFeature.TARGET_TEMPERATURE
)
@property
def is_aux_heat(self) -> bool | None:
"""Return true if aux heater.
Requires ClimateEntityFeature.AUX_HEAT.
"""
return True
async def async_turn_aux_heat_on(self) -> None:
"""Turn auxiliary heater on."""
await self.hass.async_add_executor_job(self.turn_aux_heat_on)
async def async_turn_aux_heat_off(self) -> None:
"""Turn auxiliary heater off."""
await self.hass.async_add_executor_job(self.turn_aux_heat_off)
# Fake the module is custom component or built in
MockClimateEntityWithAux.__module__ = module
climate_entity = MockClimateEntityWithAux(
name="Testing",
entity_id="climate.testing",
)
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 weather platform via config entry."""
async_add_entities([climate_entity])
mock_integration(
hass,
MockModule(
"test",
async_setup_entry=async_setup_entry_init,
partial_manifest=manifest_extra,
),
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()
assert climate_entity.state == HVACMode.HEAT
issue = issue_registry.async_get_issue("climate", "deprecated_climate_aux_test")
assert issue
assert issue.issue_domain == "test"
assert issue.issue_id == "deprecated_climate_aux_test"
assert issue.translation_key == translation_key
assert (
issue.translation_placeholders
== {"platform": "test"} | translation_placeholders_extra
)
assert (
"test::MockClimateEntityWithAux implements the `is_aux_heat` property or uses "
"the auxiliary heater methods in a subclass of ClimateEntity which is deprecated "
f"and will be unsupported from Home Assistant 2024.10. Please {report}"
) in caplog.text
# Assert we only log warning once
caplog.clear()
await hass.services.async_call(
DOMAIN,
SERVICE_SET_TEMPERATURE,
{
"entity_id": "climate.test",
"temperature": "25",
},
blocking=True,
)
await hass.async_block_till_done()
assert ("implements the `is_aux_heat` property") not in caplog.text
@pytest.mark.parametrize(
(
"manifest_extra",
"translation_key",
"translation_placeholders_extra",
"report",
"module",
),
[
(
{"issue_tracker": ISSUE_TRACKER},
"deprecated_climate_aux_url",
{"issue_tracker": ISSUE_TRACKER},
"create a bug report at https://blablabla.com",
"homeassistant.components.test.climate",
),
],
)
async def test_no_issue_aux_property_deprecated_for_core(
hass: HomeAssistant,
caplog: pytest.LogCaptureFixture,
config_flow_fixture: None,
manifest_extra: dict[str, str],
translation_key: str,
translation_placeholders_extra: dict[str, str],
report: str,
module: str,
issue_registry: ir.IssueRegistry,
) -> None:
"""Test the no issue on deprecated auxiliary heater attributes for core integrations."""
class MockClimateEntityWithAux(MockClimateEntity):
"""Mock climate class with mocked aux heater."""
_attr_supported_features = ClimateEntityFeature.AUX_HEAT
@property
def is_aux_heat(self) -> bool | None:
"""Return true if aux heater.
Requires ClimateEntityFeature.AUX_HEAT.
"""
return True
async def async_turn_aux_heat_on(self) -> None:
"""Turn auxiliary heater on."""
await self.hass.async_add_executor_job(self.turn_aux_heat_on)
async def async_turn_aux_heat_off(self) -> None:
"""Turn auxiliary heater off."""
await self.hass.async_add_executor_job(self.turn_aux_heat_off)
# Fake the module is custom component or built in
MockClimateEntityWithAux.__module__ = module
climate_entity = MockClimateEntityWithAux(
name="Testing",
entity_id="climate.testing",
)
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 weather platform via config entry."""
async_add_entities([climate_entity])
mock_integration(
hass,
MockModule(
"test",
async_setup_entry=async_setup_entry_init,
partial_manifest=manifest_extra,
),
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()
assert climate_entity.state == HVACMode.HEAT
issue = issue_registry.async_get_issue("climate", "deprecated_climate_aux_test")
assert not issue
assert (
"test::MockClimateEntityWithAux implements the `is_aux_heat` property or uses "
"the auxiliary heater methods in a subclass of ClimateEntity which is deprecated "
f"and will be unsupported from Home Assistant 2024.10. Please {report}"
) not in caplog.text
async def test_no_issue_no_aux_property(
hass: HomeAssistant,
caplog: pytest.LogCaptureFixture,
config_flow_fixture: None,
issue_registry: ir.IssueRegistry,
) -> None:
"""Test the issue is raised on deprecated auxiliary heater attributes."""
climate_entity = MockClimateEntity(
name="Testing",
entity_id="climate.testing",
)
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 weather platform via config entry."""
async_add_entities([climate_entity])
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()
assert climate_entity.state == HVACMode.HEAT
assert len(issue_registry.issues) == 0
assert (
"test::MockClimateEntityWithAux implements the `is_aux_heat` property or uses "
"the auxiliary heater methods in a subclass of ClimateEntity which is deprecated "
"and will be unsupported from Home Assistant 2024.10."
) not in caplog.text
async def test_temperature_validation(
hass: HomeAssistant, config_flow_fixture: None
) -> None:
"""Test validation for temperatures."""
class MockClimateEntityTemp(MockClimateEntity):
"""Mock climate class with mocked aux heater."""
_attr_supported_features = (
ClimateEntityFeature.FAN_MODE
| ClimateEntityFeature.PRESET_MODE
| ClimateEntityFeature.SWING_MODE
| ClimateEntityFeature.TARGET_TEMPERATURE
| ClimateEntityFeature.TARGET_TEMPERATURE_RANGE
)
_attr_target_temperature = 15
_attr_target_temperature_high = 18
_attr_target_temperature_low = 10
_attr_target_temperature_step = PRECISION_WHOLE
def set_temperature(self, **kwargs: Any) -> None:
"""Set new target temperature."""
if ATTR_TEMPERATURE in kwargs:
self._attr_target_temperature = kwargs[ATTR_TEMPERATURE]
if ATTR_TARGET_TEMP_HIGH in kwargs:
self._attr_target_temperature_high = kwargs[ATTR_TARGET_TEMP_HIGH]
self._attr_target_temperature_low = kwargs[ATTR_TARGET_TEMP_LOW]
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(
[MockClimateEntityTemp(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.attributes.get(ATTR_CURRENT_TEMPERATURE) is None
assert state.attributes.get(ATTR_MIN_TEMP) == 7
assert state.attributes.get(ATTR_MAX_TEMP) == 35
with pytest.raises(
ServiceValidationError,
match="Provided temperature 40.0 is not valid. Accepted range is 7 to 35",
) as exc:
await hass.services.async_call(
DOMAIN,
SERVICE_SET_TEMPERATURE,
{
"entity_id": "climate.test",
ATTR_TEMPERATURE: "40",
},
blocking=True,
)
assert (
str(exc.value)
== "Provided temperature 40.0 is not valid. Accepted range is 7 to 35"
)
assert exc.value.translation_key == "temp_out_of_range"
with pytest.raises(
ServiceValidationError,
match="Provided temperature 0.0 is not valid. Accepted range is 7 to 35",
) as exc:
await hass.services.async_call(
DOMAIN,
SERVICE_SET_TEMPERATURE,
{
"entity_id": "climate.test",
ATTR_TARGET_TEMP_HIGH: "25",
ATTR_TARGET_TEMP_LOW: "0",
},
blocking=True,
)
assert (
str(exc.value)
== "Provided temperature 0.0 is not valid. Accepted range is 7 to 35"
)
assert exc.value.translation_key == "temp_out_of_range"
await hass.services.async_call(
DOMAIN,
SERVICE_SET_TEMPERATURE,
{
"entity_id": "climate.test",
ATTR_TARGET_TEMP_HIGH: "25",
ATTR_TARGET_TEMP_LOW: "10",
},
blocking=True,
)
state = hass.states.get("climate.test")
assert state.attributes.get(ATTR_TARGET_TEMP_LOW) == 10
assert state.attributes.get(ATTR_TARGET_TEMP_HIGH) == 25