1
0
mirror of https://github.com/home-assistant/core.git synced 2026-05-28 03:06:30 +01:00
Files
core/tests/components/tuya/test_climate.py

355 lines
10 KiB
Python

"""Test Tuya climate platform."""
import json
from typing import Any
from unittest.mock import patch
import pytest
from syrupy.assertion import SnapshotAssertion
from syrupy.filters import props
from tuya_sharing import CustomerDevice, Manager
from homeassistant.components.climate import (
ATTR_CURRENT_TEMPERATURE,
ATTR_FAN_MODE,
ATTR_HUMIDITY,
ATTR_HVAC_MODE,
ATTR_MAX_TEMP,
ATTR_MIN_TEMP,
ATTR_PRESET_MODE,
ATTR_SWING_MODE,
ATTR_TARGET_TEMP_STEP,
ATTR_TEMPERATURE,
DOMAIN as CLIMATE_DOMAIN,
SERVICE_SET_FAN_MODE,
SERVICE_SET_HUMIDITY,
SERVICE_SET_HVAC_MODE,
SERVICE_SET_PRESET_MODE,
SERVICE_SET_SWING_MODE,
SERVICE_SET_TEMPERATURE,
SERVICE_TURN_OFF,
SERVICE_TURN_ON,
HVACMode,
)
from homeassistant.const import ATTR_ENTITY_ID, Platform
from homeassistant.core import HomeAssistant
from homeassistant.exceptions import ServiceNotSupported
from homeassistant.helpers import entity_registry as er
from homeassistant.util.unit_system import (
METRIC_SYSTEM,
US_CUSTOMARY_SYSTEM,
UnitSystem,
)
from . import initialize_entry
from tests.common import MockConfigEntry, snapshot_platform
@pytest.fixture(autouse=True)
def platform_autouse():
"""Platform fixture."""
with patch("homeassistant.components.tuya.PLATFORMS", [Platform.CLIMATE]):
yield
async def test_platform_setup_and_discovery(
hass: HomeAssistant,
mock_manager: Manager,
mock_config_entry: MockConfigEntry,
mock_devices: list[CustomerDevice],
entity_registry: er.EntityRegistry,
snapshot: SnapshotAssertion,
) -> None:
"""Test platform setup and discovery."""
await initialize_entry(hass, mock_manager, mock_config_entry, mock_devices)
await snapshot_platform(hass, entity_registry, snapshot, mock_config_entry.entry_id)
async def test_us_customary_system(
hass: HomeAssistant,
mock_manager: Manager,
mock_config_entry: MockConfigEntry,
mock_devices: list[CustomerDevice],
entity_registry: er.EntityRegistry,
snapshot: SnapshotAssertion,
) -> None:
"""Test platform setup and discovery."""
hass.config.units = US_CUSTOMARY_SYSTEM
await initialize_entry(hass, mock_manager, mock_config_entry, mock_devices)
for entity in entity_registry.entities.values():
state = hass.states.get(entity.entity_id)
assert state.attributes == snapshot(
name=entity.entity_id,
include=props(
ATTR_CURRENT_TEMPERATURE,
ATTR_MAX_TEMP,
ATTR_MIN_TEMP,
ATTR_TARGET_TEMP_STEP,
ATTR_TEMPERATURE,
),
)
@pytest.mark.parametrize(
("mock_device_code", "entity_id", "service", "service_data", "expected_commands"),
[
(
"kt_5wnlzekkstwcdsvm",
"climate.air_conditioner",
SERVICE_SET_TEMPERATURE,
{ATTR_TEMPERATURE: 22.7},
[{"code": "temp_set", "value": 23}],
),
(
"kt_5wnlzekkstwcdsvm",
"climate.air_conditioner",
SERVICE_SET_FAN_MODE,
{ATTR_FAN_MODE: 2},
[{"code": "windspeed", "value": "2"}],
),
(
"kt_5wnlzekkstwcdsvm",
"climate.air_conditioner",
SERVICE_TURN_ON,
{},
[{"code": "switch", "value": True}],
),
(
"kt_5wnlzekkstwcdsvm",
"climate.air_conditioner",
SERVICE_TURN_OFF,
{},
[{"code": "switch", "value": False}],
),
(
"kt_ibmmirhhq62mmf1g",
"climate.master_bedroom_ac",
SERVICE_SET_HVAC_MODE,
{ATTR_HVAC_MODE: HVACMode.COOL},
[{"code": "switch", "value": True}, {"code": "mode", "value": "cold"}],
),
(
"wk_gc1bxoq2hafxpa35",
"climate.polotentsosushitel",
SERVICE_SET_PRESET_MODE,
{ATTR_PRESET_MODE: "holiday"},
[{"code": "mode", "value": "holiday"}],
),
(
"kt_wxqdp6ecfkd78zzz",
"climate.mini_split",
SERVICE_SET_SWING_MODE,
{ATTR_SWING_MODE: "horizontal"},
[{"code": "switch_horizontal", "value": True}],
),
],
)
async def test_action(
hass: HomeAssistant,
mock_manager: Manager,
mock_config_entry: MockConfigEntry,
mock_device: CustomerDevice,
entity_id: str,
service: str,
service_data: dict[str, Any],
expected_commands: list[dict[str, Any]],
) -> None:
"""Test climate action."""
await initialize_entry(hass, mock_manager, mock_config_entry, mock_device)
state = hass.states.get(entity_id)
assert state is not None, f"{entity_id} does not exist"
await hass.services.async_call(
CLIMATE_DOMAIN,
service,
{
ATTR_ENTITY_ID: entity_id,
**service_data,
},
blocking=True,
)
mock_manager.send_commands.assert_called_once_with(
mock_device.id, expected_commands
)
@pytest.mark.parametrize(
"mock_device_code",
["kt_5wnlzekkstwcdsvm"],
)
@pytest.mark.parametrize(
("service", "service_data"),
[
(
SERVICE_SET_FAN_MODE,
{ATTR_FAN_MODE: 2},
),
(
SERVICE_SET_HUMIDITY,
{ATTR_HUMIDITY: 50},
),
],
)
async def test_action_not_supported(
hass: HomeAssistant,
mock_manager: Manager,
mock_config_entry: MockConfigEntry,
mock_device: CustomerDevice,
service: str,
service_data: dict[str, Any],
) -> None:
"""Test service action not supported."""
# Remove windspeed DPCode to simulate a device with no valid fan mode
mock_device.function.pop("windspeed", None)
mock_device.status_range.pop("windspeed", None)
mock_device.status.pop("windspeed", None)
entity_id = "climate.air_conditioner"
await initialize_entry(hass, mock_manager, mock_config_entry, mock_device)
state = hass.states.get(entity_id)
assert state is not None, f"{entity_id} does not exist"
assert state.attributes.get(ATTR_FAN_MODE) is None
with pytest.raises(ServiceNotSupported):
await hass.services.async_call(
CLIMATE_DOMAIN,
service,
{
ATTR_ENTITY_ID: entity_id,
**service_data,
},
blocking=True,
)
def _override_temperature_unit(
device: CustomerDevice, *, temp_set: str, temp_current: str
) -> None:
"""Override the unit of the temp_set/temp_current DPCodes."""
units = {"temp_set": temp_set, "temp_current": temp_current}
for container in (device.function, device.status_range):
for code, unit in units.items():
if code in container:
values = json.loads(container[code].values)
values["unit"] = unit
container[code].values = json.dumps(values)
@pytest.mark.parametrize("mock_device_code", ["kt_5wnlzekkstwcdsvm"])
@pytest.mark.parametrize(
(
"units",
"temp_current_native_unit",
"expected_current",
"temp_set_native_unit",
"expected_target",
"service_temperature",
"expected_set_value",
),
# Note: status in the fixture "temp_set": 23 / "temp_current": 22,
[
pytest.param(
METRIC_SYSTEM, "", -5.6, "", -5, 25, 77, id="metric-both-fahrenheit"
),
pytest.param(
METRIC_SYSTEM, "", 22.0, "", 23.0, 25, 25, id="metric-both-celsius"
),
pytest.param(
METRIC_SYSTEM,
"",
22.0,
"",
-5,
25,
77,
id="metric-set-fahrenheit-current-celsius",
),
pytest.param(
METRIC_SYSTEM,
"",
-5.6,
"",
23.0,
25,
25,
id="metric-set-celsius-current-fahrenheit",
),
pytest.param(
US_CUSTOMARY_SYSTEM, "", 22, "", 23, 70, 70, id="us-both-fahrenheit"
),
pytest.param(
US_CUSTOMARY_SYSTEM, "", 72, "", 73, 70, 21, id="us-both-celsius"
),
pytest.param(
US_CUSTOMARY_SYSTEM,
"",
72,
"",
23,
70,
70,
id="us-set-fahrenheit-current-celsius",
),
pytest.param(
US_CUSTOMARY_SYSTEM,
"",
22,
"",
73,
70,
21,
id="us-set-celsius-current-fahrenheit",
),
],
)
async def test_temperature_unit_conversion(
hass: HomeAssistant,
mock_manager: Manager,
mock_config_entry: MockConfigEntry,
mock_device: CustomerDevice,
units: UnitSystem,
temp_current_native_unit: str,
expected_current: float,
temp_set_native_unit: str,
expected_target: float,
service_temperature: float,
expected_set_value: int,
) -> None:
"""Test current/target temperature conversion from the device native unit.
The kt_5wnlzekkstwcdsvm fixture reports its temperatures with the combined
"℃ ℉" unit, so the definition falls back to the system unit. Overriding the
DPCode units exercises the conversion between the device native unit and the
entity unit, independently for temp_set/temp_current and per unit system.
"""
hass.config.units = units
_override_temperature_unit(
mock_device,
temp_set=temp_set_native_unit,
temp_current=temp_current_native_unit,
)
await initialize_entry(hass, mock_manager, mock_config_entry, mock_device)
state = hass.states.get("climate.air_conditioner")
assert state is not None
assert state.attributes[ATTR_CURRENT_TEMPERATURE] == expected_current
assert state.attributes[ATTR_TEMPERATURE] == expected_target
await hass.services.async_call(
CLIMATE_DOMAIN,
SERVICE_SET_TEMPERATURE,
{
ATTR_ENTITY_ID: "climate.air_conditioner",
ATTR_TEMPERATURE: service_temperature,
},
blocking=True,
)
mock_manager.send_commands.assert_called_once_with(
mock_device.id, [{"code": "temp_set", "value": expected_set_value}]
)