Climate validate temperature(s) out of range (#118649)

* Climate temperature out of range

* Fix test sensibo

* use temp converting for min/max

* Fix

* Fix mqtt tests

* Fix honeywell tests

* Fix Balboa tests

* Fix whirlpool test

* Fix teslemetry test

* Fix plugwise test

* Fix tplink test

* Fix generic thermostat test

* Fix modbus test

* Fix fritzbox tests

* Honewell
This commit is contained in:
G Johansson 2024-07-31 19:17:53 +02:00 committed by GitHub
parent ae9e8ca419
commit a913587eb6
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
17 changed files with 245 additions and 69 deletions

View File

@ -914,12 +914,37 @@ async def async_service_temperature_set(
"""Handle set temperature service.""" """Handle set temperature service."""
hass = entity.hass hass = entity.hass
kwargs = {} kwargs = {}
min_temp = entity.min_temp
max_temp = entity.max_temp
temp_unit = entity.temperature_unit
for value, temp in service_call.data.items(): for value, temp in service_call.data.items():
if value in CONVERTIBLE_ATTRIBUTE: if value in CONVERTIBLE_ATTRIBUTE:
kwargs[value] = TemperatureConverter.convert( kwargs[value] = check_temp = TemperatureConverter.convert(
temp, hass.config.units.temperature_unit, entity.temperature_unit temp, hass.config.units.temperature_unit, temp_unit
) )
_LOGGER.debug(
"Check valid temperature %d %s (%d %s) in range %d %s - %d %s",
check_temp,
entity.temperature_unit,
temp,
hass.config.units.temperature_unit,
min_temp,
temp_unit,
max_temp,
temp_unit,
)
if check_temp < min_temp or check_temp > max_temp:
raise ServiceValidationError(
translation_domain=DOMAIN,
translation_key="temp_out_of_range",
translation_placeholders={
"check_temp": str(check_temp),
"min_temp": str(min_temp),
"max_temp": str(max_temp),
},
)
else: else:
kwargs[value] = temp kwargs[value] = temp

View File

@ -266,6 +266,9 @@
}, },
"not_valid_fan_mode": { "not_valid_fan_mode": {
"message": "Fan mode {mode} is not valid. Valid fan modes are: {modes}." "message": "Fan mode {mode} is not valid. Valid fan modes are: {modes}."
},
"temp_out_of_range": {
"message": "Provided temperature {check_temp} is not valid. Accepted range is {min_temp} to {max_temp}."
} }
} }
} }

View File

@ -42,6 +42,7 @@ from homeassistant.helpers import (
) )
from homeassistant.helpers.device_registry import DeviceInfo from homeassistant.helpers.device_registry import DeviceInfo
from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.util.unit_conversion import TemperatureConverter
from . import HoneywellData from . import HoneywellData
from .const import ( from .const import (
@ -259,7 +260,9 @@ class HoneywellUSThermostat(ClimateEntity):
self._device.raw_ui_data["HeatLowerSetptLimit"], self._device.raw_ui_data["HeatLowerSetptLimit"],
] ]
) )
return DEFAULT_MIN_TEMP return TemperatureConverter.convert(
DEFAULT_MIN_TEMP, UnitOfTemperature.CELSIUS, self.temperature_unit
)
@property @property
def max_temp(self) -> float: def max_temp(self) -> float:
@ -275,7 +278,9 @@ class HoneywellUSThermostat(ClimateEntity):
self._device.raw_ui_data["HeatUpperSetptLimit"], self._device.raw_ui_data["HeatUpperSetptLimit"],
] ]
) )
return DEFAULT_MAX_TEMP return TemperatureConverter.convert(
DEFAULT_MAX_TEMP, UnitOfTemperature.CELSIUS, self.temperature_unit
)
@property @property
def current_humidity(self) -> int | None: def current_humidity(self) -> int | None:

View File

@ -85,6 +85,8 @@ async def test_spa_temperature(
hass: HomeAssistant, client: MagicMock, integration: MockConfigEntry hass: HomeAssistant, client: MagicMock, integration: MockConfigEntry
) -> None: ) -> None:
"""Test spa temperature settings.""" """Test spa temperature settings."""
client.temperature_minimum = 110
client.temperature_maximum = 250
# flip the spa into F # flip the spa into F
# set temp to a valid number # set temp to a valid number
state = await _patch_spa_settemp(hass, client, 0, 100) state = await _patch_spa_settemp(hass, client, 0, 100)

View File

@ -4,6 +4,7 @@ from __future__ import annotations
from enum import Enum from enum import Enum
from types import ModuleType from types import ModuleType
from typing import Any
from unittest.mock import MagicMock, Mock, patch from unittest.mock import MagicMock, Mock, patch
import pytest import pytest
@ -17,9 +18,14 @@ from homeassistant.components.climate import (
HVACMode, HVACMode,
) )
from homeassistant.components.climate.const import ( from homeassistant.components.climate.const import (
ATTR_CURRENT_TEMPERATURE,
ATTR_FAN_MODE, ATTR_FAN_MODE,
ATTR_MAX_TEMP,
ATTR_MIN_TEMP,
ATTR_PRESET_MODE, ATTR_PRESET_MODE,
ATTR_SWING_MODE, ATTR_SWING_MODE,
ATTR_TARGET_TEMP_HIGH,
ATTR_TARGET_TEMP_LOW,
SERVICE_SET_FAN_MODE, SERVICE_SET_FAN_MODE,
SERVICE_SET_PRESET_MODE, SERVICE_SET_PRESET_MODE,
SERVICE_SET_SWING_MODE, SERVICE_SET_SWING_MODE,
@ -27,7 +33,13 @@ from homeassistant.components.climate.const import (
ClimateEntityFeature, ClimateEntityFeature,
) )
from homeassistant.config_entries import ConfigEntry from homeassistant.config_entries import ConfigEntry
from homeassistant.const import SERVICE_TURN_OFF, SERVICE_TURN_ON, UnitOfTemperature from homeassistant.const import (
ATTR_TEMPERATURE,
PRECISION_WHOLE,
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 import issue_registry as ir from homeassistant.helpers import issue_registry as ir
@ -1152,3 +1164,127 @@ async def test_no_issue_no_aux_property(
"the auxiliary heater methods in a subclass of ClimateEntity which is deprecated " "the auxiliary heater methods in a subclass of ClimateEntity which is deprecated "
"and will be unsupported from Home Assistant 2024.10." "and will be unsupported from Home Assistant 2024.10."
) not in caplog.text ) 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

View File

@ -263,10 +263,10 @@ async def test_set_temperature_temperature(hass: HomeAssistant, fritz: Mock) ->
await hass.services.async_call( await hass.services.async_call(
DOMAIN, DOMAIN,
SERVICE_SET_TEMPERATURE, SERVICE_SET_TEMPERATURE,
{ATTR_ENTITY_ID: ENTITY_ID, ATTR_TEMPERATURE: 123}, {ATTR_ENTITY_ID: ENTITY_ID, ATTR_TEMPERATURE: 23},
True, True,
) )
assert device.set_target_temperature.call_args_list == [call(123)] assert device.set_target_temperature.call_args_list == [call(23)]
async def test_set_temperature_mode_off(hass: HomeAssistant, fritz: Mock) -> None: async def test_set_temperature_mode_off(hass: HomeAssistant, fritz: Mock) -> None:
@ -282,7 +282,7 @@ async def test_set_temperature_mode_off(hass: HomeAssistant, fritz: Mock) -> Non
{ {
ATTR_ENTITY_ID: ENTITY_ID, ATTR_ENTITY_ID: ENTITY_ID,
ATTR_HVAC_MODE: HVACMode.OFF, ATTR_HVAC_MODE: HVACMode.OFF,
ATTR_TEMPERATURE: 123, ATTR_TEMPERATURE: 23,
}, },
True, True,
) )
@ -303,7 +303,7 @@ async def test_set_temperature_mode_heat(hass: HomeAssistant, fritz: Mock) -> No
{ {
ATTR_ENTITY_ID: ENTITY_ID, ATTR_ENTITY_ID: ENTITY_ID,
ATTR_HVAC_MODE: HVACMode.HEAT, ATTR_HVAC_MODE: HVACMode.HEAT,
ATTR_TEMPERATURE: 123, ATTR_TEMPERATURE: 23,
}, },
True, True,
) )

View File

@ -1097,9 +1097,9 @@ async def setup_comp_9(hass: HomeAssistant) -> None:
async def test_precision(hass: HomeAssistant) -> None: async def test_precision(hass: HomeAssistant) -> None:
"""Test that setting precision to tenths works as intended.""" """Test that setting precision to tenths works as intended."""
hass.config.units = US_CUSTOMARY_SYSTEM hass.config.units = US_CUSTOMARY_SYSTEM
await common.async_set_temperature(hass, 23.27) await common.async_set_temperature(hass, 55.27)
state = hass.states.get(ENTITY) state = hass.states.get(ENTITY)
assert state.attributes.get("temperature") == 23.3 assert state.attributes.get("temperature") == 55.3
# check that target_temp_step defaults to precision # check that target_temp_step defaults to precision
assert state.attributes.get("target_temp_step") == 0.1 assert state.attributes.get("target_temp_step") == 0.1

View File

@ -86,6 +86,7 @@ def device():
mock_device.system_mode = "off" mock_device.system_mode = "off"
mock_device.name = "device1" mock_device.name = "device1"
mock_device.current_temperature = CURRENTTEMPERATURE mock_device.current_temperature = CURRENTTEMPERATURE
mock_device.temperature_unit = "C"
mock_device.mac_address = "macaddress1" mock_device.mac_address = "macaddress1"
mock_device.outdoor_temperature = None mock_device.outdoor_temperature = None
mock_device.outdoor_humidity = None mock_device.outdoor_humidity = None

View File

@ -3,7 +3,7 @@
ReadOnlyDict({ ReadOnlyDict({
'aux_heat': 'off', 'aux_heat': 'off',
'current_humidity': 50, 'current_humidity': 50,
'current_temperature': -6.7, 'current_temperature': 20,
'fan_action': 'idle', 'fan_action': 'idle',
'fan_mode': 'auto', 'fan_mode': 'auto',
'fan_modes': list([ 'fan_modes': list([
@ -20,9 +20,9 @@
<HVACMode.HEAT: 'heat'>, <HVACMode.HEAT: 'heat'>,
]), ]),
'max_humidity': 99, 'max_humidity': 99,
'max_temp': 1.7, 'max_temp': 35,
'min_humidity': 30, 'min_humidity': 30,
'min_temp': -13.9, 'min_temp': 7,
'permanent_hold': False, 'permanent_hold': False,
'preset_mode': 'none', 'preset_mode': 'none',
'preset_modes': list([ 'preset_modes': list([

View File

@ -92,14 +92,13 @@ async def test_dynamic_attributes(
hass: HomeAssistant, device: MagicMock, config_entry: MagicMock hass: HomeAssistant, device: MagicMock, config_entry: MagicMock
) -> None: ) -> None:
"""Test dynamic attributes.""" """Test dynamic attributes."""
await init_integration(hass, config_entry) await init_integration(hass, config_entry)
entity_id = f"climate.{device.name}" entity_id = f"climate.{device.name}"
state = hass.states.get(entity_id) state = hass.states.get(entity_id)
assert state.state == HVACMode.OFF assert state.state == HVACMode.OFF
attributes = state.attributes attributes = state.attributes
assert attributes["current_temperature"] == -6.7 assert attributes["current_temperature"] == 20
assert attributes["current_humidity"] == 50 assert attributes["current_humidity"] == 50
device.system_mode = "cool" device.system_mode = "cool"
@ -114,7 +113,7 @@ async def test_dynamic_attributes(
state = hass.states.get(entity_id) state = hass.states.get(entity_id)
assert state.state == HVACMode.COOL assert state.state == HVACMode.COOL
attributes = state.attributes attributes = state.attributes
assert attributes["current_temperature"] == -6.1 assert attributes["current_temperature"] == 21
assert attributes["current_humidity"] == 55 assert attributes["current_humidity"] == 55
device.system_mode = "heat" device.system_mode = "heat"
@ -129,7 +128,7 @@ async def test_dynamic_attributes(
state = hass.states.get(entity_id) state = hass.states.get(entity_id)
assert state.state == HVACMode.HEAT assert state.state == HVACMode.HEAT
attributes = state.attributes attributes = state.attributes
assert attributes["current_temperature"] == 16.1 assert attributes["current_temperature"] == 61
assert attributes["current_humidity"] == 50 assert attributes["current_humidity"] == 50
device.system_mode = "auto" device.system_mode = "auto"
@ -142,7 +141,7 @@ async def test_dynamic_attributes(
state = hass.states.get(entity_id) state = hass.states.get(entity_id)
assert state.state == HVACMode.HEAT_COOL assert state.state == HVACMode.HEAT_COOL
attributes = state.attributes attributes = state.attributes
assert attributes["current_temperature"] == 16.1 assert attributes["current_temperature"] == 61
assert attributes["current_humidity"] == 50 assert attributes["current_humidity"] == 50
@ -348,7 +347,7 @@ async def test_service_calls_off_mode(
await hass.services.async_call( await hass.services.async_call(
CLIMATE_DOMAIN, CLIMATE_DOMAIN,
SERVICE_SET_TEMPERATURE, SERVICE_SET_TEMPERATURE,
{ATTR_ENTITY_ID: entity_id, ATTR_TEMPERATURE: 15}, {ATTR_ENTITY_ID: entity_id, ATTR_TEMPERATURE: 35},
blocking=True, blocking=True,
) )
@ -362,8 +361,8 @@ async def test_service_calls_off_mode(
}, },
blocking=True, blocking=True,
) )
device.set_setpoint_cool.assert_called_with(95) device.set_setpoint_cool.assert_called_with(35)
device.set_setpoint_heat.assert_called_with(77) device.set_setpoint_heat.assert_called_with(25)
device.set_setpoint_heat.reset_mock() device.set_setpoint_heat.reset_mock()
device.set_setpoint_heat.side_effect = aiosomecomfort.SomeComfortError device.set_setpoint_heat.side_effect = aiosomecomfort.SomeComfortError
@ -375,13 +374,13 @@ async def test_service_calls_off_mode(
SERVICE_SET_TEMPERATURE, SERVICE_SET_TEMPERATURE,
{ {
ATTR_ENTITY_ID: entity_id, ATTR_ENTITY_ID: entity_id,
ATTR_TARGET_TEMP_LOW: 25.0, ATTR_TARGET_TEMP_LOW: 24.0,
ATTR_TARGET_TEMP_HIGH: 35.0, ATTR_TARGET_TEMP_HIGH: 34.0,
}, },
blocking=True, blocking=True,
) )
device.set_setpoint_cool.assert_called_with(95) device.set_setpoint_cool.assert_called_with(34)
device.set_setpoint_heat.assert_called_with(77) device.set_setpoint_heat.assert_called_with(24)
assert "Invalid temperature" in caplog.text assert "Invalid temperature" in caplog.text
device.set_setpoint_heat.reset_mock() device.set_setpoint_heat.reset_mock()
@ -399,14 +398,14 @@ async def test_service_calls_off_mode(
}, },
blocking=True, blocking=True,
) )
device.set_setpoint_cool.assert_called_with(95) device.set_setpoint_cool.assert_called_with(35)
device.set_setpoint_heat.assert_called_with(77) device.set_setpoint_heat.assert_called_with(25)
reset_mock(device) reset_mock(device)
await hass.services.async_call( await hass.services.async_call(
CLIMATE_DOMAIN, CLIMATE_DOMAIN,
SERVICE_SET_TEMPERATURE, SERVICE_SET_TEMPERATURE,
{ATTR_ENTITY_ID: entity_id, ATTR_TEMPERATURE: 15}, {ATTR_ENTITY_ID: entity_id, ATTR_TEMPERATURE: 35},
blocking=True, blocking=True,
) )
device.set_setpoint_heat.assert_not_called() device.set_setpoint_heat.assert_not_called()
@ -517,7 +516,7 @@ async def test_service_calls_cool_mode(
{ATTR_ENTITY_ID: entity_id, ATTR_TEMPERATURE: 15}, {ATTR_ENTITY_ID: entity_id, ATTR_TEMPERATURE: 15},
blocking=True, blocking=True,
) )
device.set_hold_cool.assert_called_once_with(datetime.time(2, 30), 59) device.set_hold_cool.assert_called_once_with(datetime.time(2, 30), 15)
device.set_hold_cool.reset_mock() device.set_hold_cool.reset_mock()
await hass.services.async_call( await hass.services.async_call(
@ -525,13 +524,13 @@ async def test_service_calls_cool_mode(
SERVICE_SET_TEMPERATURE, SERVICE_SET_TEMPERATURE,
{ {
ATTR_ENTITY_ID: entity_id, ATTR_ENTITY_ID: entity_id,
ATTR_TARGET_TEMP_LOW: 25.0, ATTR_TARGET_TEMP_LOW: 15.0,
ATTR_TARGET_TEMP_HIGH: 35.0, ATTR_TARGET_TEMP_HIGH: 20.0,
}, },
blocking=True, blocking=True,
) )
device.set_setpoint_cool.assert_called_with(95) device.set_setpoint_cool.assert_called_with(20)
device.set_setpoint_heat.assert_called_with(77) device.set_setpoint_heat.assert_called_with(15)
caplog.clear() caplog.clear()
device.set_setpoint_cool.reset_mock() device.set_setpoint_cool.reset_mock()
@ -543,13 +542,13 @@ async def test_service_calls_cool_mode(
SERVICE_SET_TEMPERATURE, SERVICE_SET_TEMPERATURE,
{ {
ATTR_ENTITY_ID: entity_id, ATTR_ENTITY_ID: entity_id,
ATTR_TARGET_TEMP_LOW: 25.0, ATTR_TARGET_TEMP_LOW: 15.0,
ATTR_TARGET_TEMP_HIGH: 35.0, ATTR_TARGET_TEMP_HIGH: 20.0,
}, },
blocking=True, blocking=True,
) )
device.set_setpoint_cool.assert_called_with(95) device.set_setpoint_cool.assert_called_with(20)
device.set_setpoint_heat.assert_called_with(77) device.set_setpoint_heat.assert_called_with(15)
assert "Invalid temperature" in caplog.text assert "Invalid temperature" in caplog.text
reset_mock(device) reset_mock(device)
@ -733,10 +732,10 @@ async def test_service_calls_heat_mode(
await hass.services.async_call( await hass.services.async_call(
CLIMATE_DOMAIN, CLIMATE_DOMAIN,
SERVICE_SET_TEMPERATURE, SERVICE_SET_TEMPERATURE,
{ATTR_ENTITY_ID: entity_id, ATTR_TEMPERATURE: 15}, {ATTR_ENTITY_ID: entity_id, ATTR_TEMPERATURE: 25},
blocking=True, blocking=True,
) )
device.set_hold_heat.assert_called_once_with(datetime.time(2, 30), 59) device.set_hold_heat.assert_called_once_with(datetime.time(2, 30), 25)
device.set_hold_heat.reset_mock() device.set_hold_heat.reset_mock()
device.set_hold_heat.side_effect = aiosomecomfort.SomeComfortError device.set_hold_heat.side_effect = aiosomecomfort.SomeComfortError
@ -744,10 +743,10 @@ async def test_service_calls_heat_mode(
await hass.services.async_call( await hass.services.async_call(
CLIMATE_DOMAIN, CLIMATE_DOMAIN,
SERVICE_SET_TEMPERATURE, SERVICE_SET_TEMPERATURE,
{ATTR_ENTITY_ID: entity_id, ATTR_TEMPERATURE: 15}, {ATTR_ENTITY_ID: entity_id, ATTR_TEMPERATURE: 25},
blocking=True, blocking=True,
) )
device.set_hold_heat.assert_called_once_with(datetime.time(2, 30), 59) device.set_hold_heat.assert_called_once_with(datetime.time(2, 30), 25)
device.set_hold_heat.reset_mock() device.set_hold_heat.reset_mock()
assert "Invalid temperature" in caplog.text assert "Invalid temperature" in caplog.text
@ -756,10 +755,10 @@ async def test_service_calls_heat_mode(
await hass.services.async_call( await hass.services.async_call(
CLIMATE_DOMAIN, CLIMATE_DOMAIN,
SERVICE_SET_TEMPERATURE, SERVICE_SET_TEMPERATURE,
{ATTR_ENTITY_ID: entity_id, ATTR_TEMPERATURE: 15}, {ATTR_ENTITY_ID: entity_id, ATTR_TEMPERATURE: 25},
blocking=True, blocking=True,
) )
device.set_hold_heat.assert_called_once_with(datetime.time(2, 30), 59) device.set_hold_heat.assert_called_once_with(datetime.time(2, 30), 25)
device.set_hold_heat.reset_mock() device.set_hold_heat.reset_mock()
caplog.clear() caplog.clear()
@ -773,8 +772,8 @@ async def test_service_calls_heat_mode(
}, },
blocking=True, blocking=True,
) )
device.set_setpoint_cool.assert_called_with(95) device.set_setpoint_cool.assert_called_with(35)
device.set_setpoint_heat.assert_called_with(77) device.set_setpoint_heat.assert_called_with(25)
device.set_setpoint_heat.reset_mock() device.set_setpoint_heat.reset_mock()
device.set_setpoint_heat.side_effect = aiosomecomfort.SomeComfortError device.set_setpoint_heat.side_effect = aiosomecomfort.SomeComfortError
@ -789,8 +788,8 @@ async def test_service_calls_heat_mode(
}, },
blocking=True, blocking=True,
) )
device.set_setpoint_cool.assert_called_with(95) device.set_setpoint_cool.assert_called_with(35)
device.set_setpoint_heat.assert_called_with(77) device.set_setpoint_heat.assert_called_with(25)
assert "Invalid temperature" in caplog.text assert "Invalid temperature" in caplog.text
reset_mock(device) reset_mock(device)
@ -984,8 +983,8 @@ async def test_service_calls_auto_mode(
}, },
blocking=True, blocking=True,
) )
device.set_setpoint_cool.assert_called_once_with(95) device.set_setpoint_cool.assert_called_once_with(35)
device.set_setpoint_heat.assert_called_once_with(77) device.set_setpoint_heat.assert_called_once_with(25)
reset_mock(device) reset_mock(device)
caplog.clear() caplog.clear()

View File

@ -766,7 +766,7 @@ async def test_service_climate_swing_update(
("temperature", "result", "do_config"), ("temperature", "result", "do_config"),
[ [
( (
35, 31,
[0x00], [0x00],
{ {
CONF_CLIMATES: [ CONF_CLIMATES: [
@ -781,7 +781,7 @@ async def test_service_climate_swing_update(
}, },
), ),
( (
36, 32,
[0x00, 0x00], [0x00, 0x00],
{ {
CONF_CLIMATES: [ CONF_CLIMATES: [
@ -796,7 +796,7 @@ async def test_service_climate_swing_update(
}, },
), ),
( (
37.5, 33.5,
[0x00, 0x00], [0x00, 0x00],
{ {
CONF_CLIMATES: [ CONF_CLIMATES: [
@ -811,7 +811,7 @@ async def test_service_climate_swing_update(
}, },
), ),
( (
"39", "34",
[0x00, 0x00, 0x00, 0x00], [0x00, 0x00, 0x00, 0x00],
{ {
CONF_CLIMATES: [ CONF_CLIMATES: [

View File

@ -654,11 +654,11 @@ async def test_set_target_temperature(
assert state.state == "heat" assert state.state == "heat"
mqtt_mock.async_publish.assert_called_once_with("mode-topic", "heat", 0, False) mqtt_mock.async_publish.assert_called_once_with("mode-topic", "heat", 0, False)
mqtt_mock.async_publish.reset_mock() mqtt_mock.async_publish.reset_mock()
await common.async_set_temperature(hass, temperature=47, entity_id=ENTITY_CLIMATE) await common.async_set_temperature(hass, temperature=35, entity_id=ENTITY_CLIMATE)
state = hass.states.get(ENTITY_CLIMATE) state = hass.states.get(ENTITY_CLIMATE)
assert state.attributes.get("temperature") == 47 assert state.attributes.get("temperature") == 35
mqtt_mock.async_publish.assert_called_once_with( mqtt_mock.async_publish.assert_called_once_with(
"temperature-topic", "47.0", 0, False "temperature-topic", "35.0", 0, False
) )
# also test directly supplying the operation mode to set_temperature # also test directly supplying the operation mode to set_temperature
@ -713,7 +713,7 @@ async def test_set_target_temperature_pessimistic(
state = hass.states.get(ENTITY_CLIMATE) state = hass.states.get(ENTITY_CLIMATE)
assert state.attributes.get("temperature") is None assert state.attributes.get("temperature") is None
await common.async_set_hvac_mode(hass, "heat", ENTITY_CLIMATE) await common.async_set_hvac_mode(hass, "heat", ENTITY_CLIMATE)
await common.async_set_temperature(hass, temperature=47, entity_id=ENTITY_CLIMATE) await common.async_set_temperature(hass, temperature=35, entity_id=ENTITY_CLIMATE)
state = hass.states.get(ENTITY_CLIMATE) state = hass.states.get(ENTITY_CLIMATE)
assert state.attributes.get("temperature") is None assert state.attributes.get("temperature") is None
@ -1590,13 +1590,13 @@ async def test_set_and_templates(
assert state.attributes.get("swing_mode") == "on" assert state.attributes.get("swing_mode") == "on"
# Temperature # Temperature
await common.async_set_temperature(hass, temperature=47, entity_id=ENTITY_CLIMATE) await common.async_set_temperature(hass, temperature=35, entity_id=ENTITY_CLIMATE)
mqtt_mock.async_publish.assert_called_once_with( mqtt_mock.async_publish.assert_called_once_with(
"temperature-topic", "temp: 47.0", 0, False "temperature-topic", "temp: 35.0", 0, False
) )
mqtt_mock.async_publish.reset_mock() mqtt_mock.async_publish.reset_mock()
state = hass.states.get(ENTITY_CLIMATE) state = hass.states.get(ENTITY_CLIMATE)
assert state.attributes.get("temperature") == 47 assert state.attributes.get("temperature") == 35
# Temperature Low/High # Temperature Low/High
await common.async_set_temperature( await common.async_set_temperature(

View File

@ -14,7 +14,7 @@ from homeassistant.components.climate import (
HVACMode, HVACMode,
) )
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from homeassistant.exceptions import HomeAssistantError from homeassistant.exceptions import HomeAssistantError, ServiceValidationError
from homeassistant.util.dt import utcnow from homeassistant.util.dt import utcnow
from tests.common import MockConfigEntry, async_fire_time_changed from tests.common import MockConfigEntry, async_fire_time_changed
@ -196,7 +196,7 @@ async def test_adam_climate_entity_climate_changes(
"c50f167537524366a5af7aa3942feb1e", {"setpoint": 25.0} "c50f167537524366a5af7aa3942feb1e", {"setpoint": 25.0}
) )
with pytest.raises(ValueError): with pytest.raises(ServiceValidationError):
await hass.services.async_call( await hass.services.async_call(
CLIMATE_DOMAIN, CLIMATE_DOMAIN,
SERVICE_SET_TEMPERATURE, SERVICE_SET_TEMPERATURE,

View File

@ -400,6 +400,10 @@ async def test_climate_temperatures(
"homeassistant.components.sensibo.util.SensiboClient.async_set_ac_state_property", "homeassistant.components.sensibo.util.SensiboClient.async_set_ac_state_property",
return_value={"result": {"status": "Success"}}, return_value={"result": {"status": "Success"}},
), ),
pytest.raises(
ServiceValidationError,
match="Provided temperature 24.0 is not valid. Accepted range is 10 to 20",
),
): ):
await hass.services.async_call( await hass.services.async_call(
CLIMATE_DOMAIN, CLIMATE_DOMAIN,
@ -410,7 +414,7 @@ async def test_climate_temperatures(
await hass.async_block_till_done() await hass.async_block_till_done()
state2 = hass.states.get("climate.hallway") state2 = hass.states.get("climate.hallway")
assert state2.attributes["temperature"] == 20 assert state2.attributes["temperature"] == 19
with ( with (
patch( patch(

View File

@ -199,7 +199,7 @@ async def test_climate(
await hass.services.async_call( await hass.services.async_call(
CLIMATE_DOMAIN, CLIMATE_DOMAIN,
SERVICE_SET_TEMPERATURE, SERVICE_SET_TEMPERATURE,
{ATTR_ENTITY_ID: [entity_id], ATTR_TEMPERATURE: 25}, {ATTR_ENTITY_ID: [entity_id], ATTR_TEMPERATURE: 34},
blocking=True, blocking=True,
) )

View File

@ -120,12 +120,13 @@ async def test_set_temperature(
hass: HomeAssistant, mock_config_entry: MockConfigEntry, mocked_hub: Device hass: HomeAssistant, mock_config_entry: MockConfigEntry, mocked_hub: Device
) -> None: ) -> None:
"""Test that set_temperature service calls the setter.""" """Test that set_temperature service calls the setter."""
mocked_thermostat = mocked_hub.children[0]
mocked_thermostat.features["target_temperature"].minimum_value = 0
await setup_platform_for_device( await setup_platform_for_device(
hass, mock_config_entry, Platform.CLIMATE, mocked_hub hass, mock_config_entry, Platform.CLIMATE, mocked_hub
) )
mocked_thermostat = mocked_hub.children[0]
await hass.services.async_call( await hass.services.async_call(
CLIMATE_DOMAIN, CLIMATE_DOMAIN,
SERVICE_SET_TEMPERATURE, SERVICE_SET_TEMPERATURE,

View File

@ -264,10 +264,10 @@ async def test_service_calls(
await hass.services.async_call( await hass.services.async_call(
CLIMATE_DOMAIN, CLIMATE_DOMAIN,
SERVICE_SET_TEMPERATURE, SERVICE_SET_TEMPERATURE,
{ATTR_ENTITY_ID: entity_id, ATTR_TEMPERATURE: 15}, {ATTR_ENTITY_ID: entity_id, ATTR_TEMPERATURE: 16},
blocking=True, blocking=True,
) )
mock_instance.set_temp.assert_called_once_with(15) mock_instance.set_temp.assert_called_once_with(16)
mock_instance.set_mode.reset_mock() mock_instance.set_mode.reset_mock()
await hass.services.async_call( await hass.services.async_call(