core/tests/components/matter/test_climate.py

351 lines
12 KiB
Python

"""Test Matter locks."""
from unittest.mock import MagicMock, call
from chip.clusters import Objects as clusters
from matter_server.client.models.node import MatterNode
from matter_server.common.helpers.util import create_attribute_path_from_attribute
import pytest
from syrupy import SnapshotAssertion
from homeassistant.components.climate import ClimateEntityFeature, HVACAction, HVACMode
from homeassistant.const import Platform
from homeassistant.core import HomeAssistant
from homeassistant.helpers import entity_registry as er
from .common import (
set_node_attribute,
snapshot_matter_entities,
trigger_subscription_callback,
)
@pytest.mark.usefixtures("matter_devices")
async def test_climates(
hass: HomeAssistant,
entity_registry: er.EntityRegistry,
snapshot: SnapshotAssertion,
) -> None:
"""Test climates."""
snapshot_matter_entities(hass, entity_registry, snapshot, Platform.CLIMATE)
@pytest.mark.parametrize("node_fixture", ["thermostat"])
async def test_thermostat_base(
hass: HomeAssistant,
matter_client: MagicMock,
matter_node: MatterNode,
) -> None:
"""Test thermostat base attributes and state updates."""
# test entity attributes
state = hass.states.get("climate.longan_link_hvac")
assert state
assert state.attributes["min_temp"] == 7
assert state.attributes["max_temp"] == 35
assert state.attributes["temperature"] is None
assert state.state == HVACMode.COOL
# test supported features correctly parsed
# including temperature_range support
mask = (
ClimateEntityFeature.TARGET_TEMPERATURE
| ClimateEntityFeature.TURN_OFF
| ClimateEntityFeature.TARGET_TEMPERATURE_RANGE
)
assert state.attributes["supported_features"] & mask == mask
# test common state updates from device
set_node_attribute(matter_node, 1, 513, 3, 1600)
set_node_attribute(matter_node, 1, 513, 4, 3000)
set_node_attribute(matter_node, 1, 513, 5, 1600)
set_node_attribute(matter_node, 1, 513, 6, 3000)
await trigger_subscription_callback(hass, matter_client)
state = hass.states.get("climate.longan_link_hvac")
assert state
assert state.attributes["min_temp"] == 16
assert state.attributes["max_temp"] == 30
assert state.attributes["hvac_modes"] == [
HVACMode.OFF,
HVACMode.HEAT,
HVACMode.COOL,
HVACMode.HEAT_COOL,
]
# test system mode update from device
set_node_attribute(matter_node, 1, 513, 28, 0)
await trigger_subscription_callback(hass, matter_client)
state = hass.states.get("climate.longan_link_hvac")
assert state
assert state.state == HVACMode.OFF
# test running state update from device
set_node_attribute(matter_node, 1, 513, 41, 1)
await trigger_subscription_callback(hass, matter_client)
state = hass.states.get("climate.longan_link_hvac")
assert state
assert state.attributes["hvac_action"] == HVACAction.HEATING
set_node_attribute(matter_node, 1, 513, 41, 8)
await trigger_subscription_callback(hass, matter_client)
state = hass.states.get("climate.longan_link_hvac")
assert state
assert state.attributes["hvac_action"] == HVACAction.HEATING
set_node_attribute(matter_node, 1, 513, 41, 2)
await trigger_subscription_callback(hass, matter_client)
state = hass.states.get("climate.longan_link_hvac")
assert state
assert state.attributes["hvac_action"] == HVACAction.COOLING
set_node_attribute(matter_node, 1, 513, 41, 16)
await trigger_subscription_callback(hass, matter_client)
state = hass.states.get("climate.longan_link_hvac")
assert state
assert state.attributes["hvac_action"] == HVACAction.COOLING
set_node_attribute(matter_node, 1, 513, 41, 4)
await trigger_subscription_callback(hass, matter_client)
state = hass.states.get("climate.longan_link_hvac")
assert state
assert state.attributes["hvac_action"] == HVACAction.FAN
set_node_attribute(matter_node, 1, 513, 41, 32)
await trigger_subscription_callback(hass, matter_client)
state = hass.states.get("climate.longan_link_hvac")
assert state
assert state.attributes["hvac_action"] == HVACAction.FAN
set_node_attribute(matter_node, 1, 513, 41, 64)
await trigger_subscription_callback(hass, matter_client)
state = hass.states.get("climate.longan_link_hvac")
assert state
assert state.attributes["hvac_action"] == HVACAction.FAN
set_node_attribute(matter_node, 1, 513, 41, 66)
await trigger_subscription_callback(hass, matter_client)
state = hass.states.get("climate.longan_link_hvac")
assert state
assert state.attributes["hvac_action"] == HVACAction.OFF
# change system mode to heat
set_node_attribute(matter_node, 1, 513, 28, 4)
await trigger_subscription_callback(hass, matter_client)
state = hass.states.get("climate.longan_link_hvac")
assert state
assert state.state == HVACMode.HEAT
# change occupied heating setpoint to 20
set_node_attribute(matter_node, 1, 513, 18, 2000)
await trigger_subscription_callback(hass, matter_client)
state = hass.states.get("climate.longan_link_hvac")
assert state
assert state.attributes["temperature"] == 20
@pytest.mark.parametrize("node_fixture", ["thermostat"])
async def test_thermostat_service_calls(
hass: HomeAssistant,
matter_client: MagicMock,
matter_node: MatterNode,
) -> None:
"""Test climate platform service calls."""
# test single-setpoint temperature adjustment when cool mode is active
state = hass.states.get("climate.longan_link_hvac")
assert state
assert state.state == HVACMode.COOL
await hass.services.async_call(
"climate",
"set_temperature",
{
"entity_id": "climate.longan_link_hvac",
"temperature": 25,
},
blocking=True,
)
assert matter_client.write_attribute.call_count == 1
assert matter_client.write_attribute.call_args == call(
node_id=matter_node.node_id,
attribute_path="1/513/17",
value=2500,
)
matter_client.write_attribute.reset_mock()
# ensure that no command is executed when the temperature is the same
set_node_attribute(matter_node, 1, 513, 17, 2500)
await trigger_subscription_callback(hass, matter_client)
await hass.services.async_call(
"climate",
"set_temperature",
{
"entity_id": "climate.longan_link_hvac",
"temperature": 25,
},
blocking=True,
)
assert matter_client.write_attribute.call_count == 0
matter_client.write_attribute.reset_mock()
# test single-setpoint temperature adjustment when heat mode is active
set_node_attribute(matter_node, 1, 513, 28, 4)
await trigger_subscription_callback(hass, matter_client)
state = hass.states.get("climate.longan_link_hvac")
assert state
assert state.state == HVACMode.HEAT
await hass.services.async_call(
"climate",
"set_temperature",
{
"entity_id": "climate.longan_link_hvac",
"temperature": 20,
},
blocking=True,
)
assert matter_client.write_attribute.call_count == 1
assert matter_client.write_attribute.call_args == call(
node_id=matter_node.node_id,
attribute_path="1/513/18",
value=2000,
)
matter_client.write_attribute.reset_mock()
# test dual setpoint temperature adjustments when heat_cool mode is active
set_node_attribute(matter_node, 1, 513, 28, 1)
await trigger_subscription_callback(hass, matter_client)
state = hass.states.get("climate.longan_link_hvac")
assert state
assert state.state == HVACMode.HEAT_COOL
await hass.services.async_call(
"climate",
"set_temperature",
{
"entity_id": "climate.longan_link_hvac",
"target_temp_low": 10,
"target_temp_high": 30,
},
blocking=True,
)
assert matter_client.write_attribute.call_count == 2
assert matter_client.write_attribute.call_args_list[0] == call(
node_id=matter_node.node_id,
attribute_path="1/513/18",
value=1000,
)
assert matter_client.write_attribute.call_args_list[1] == call(
node_id=matter_node.node_id,
attribute_path="1/513/17",
value=3000,
)
matter_client.write_attribute.reset_mock()
# test change HAVC mode to heat
await hass.services.async_call(
"climate",
"set_hvac_mode",
{
"entity_id": "climate.longan_link_hvac",
"hvac_mode": HVACMode.HEAT,
},
blocking=True,
)
assert matter_client.write_attribute.call_count == 1
assert matter_client.write_attribute.call_args == call(
node_id=matter_node.node_id,
attribute_path=create_attribute_path_from_attribute(
endpoint_id=1,
attribute=clusters.Thermostat.Attributes.SystemMode,
),
value=4,
)
matter_client.send_device_command.reset_mock()
# change target_temp and hvac_mode in the same call
matter_client.send_device_command.reset_mock()
matter_client.write_attribute.reset_mock()
await hass.services.async_call(
"climate",
"set_temperature",
{
"entity_id": "climate.longan_link_hvac",
"temperature": 22,
"hvac_mode": HVACMode.COOL,
},
blocking=True,
)
assert matter_client.write_attribute.call_count == 2
assert matter_client.write_attribute.call_args_list[0] == call(
node_id=matter_node.node_id,
attribute_path=create_attribute_path_from_attribute(
endpoint_id=1,
attribute=clusters.Thermostat.Attributes.SystemMode,
),
value=3,
)
assert matter_client.write_attribute.call_args_list[1] == call(
node_id=matter_node.node_id,
attribute_path="1/513/17",
value=2200,
)
matter_client.write_attribute.reset_mock()
@pytest.mark.parametrize("node_fixture", ["room_airconditioner"])
async def test_room_airconditioner(
hass: HomeAssistant,
matter_client: MagicMock,
matter_node: MatterNode,
) -> None:
"""Test if a climate entity is created for a Room Airconditioner device."""
state = hass.states.get("climate.room_airconditioner")
assert state
assert state.attributes["current_temperature"] == 20
# room airconditioner has mains power on OnOff cluster with value set to False
assert state.state == HVACMode.OFF
# test supported features correctly parsed
# WITHOUT temperature_range support
mask = ClimateEntityFeature.TARGET_TEMPERATURE | ClimateEntityFeature.TURN_OFF
assert state.attributes["supported_features"] & mask == mask
# set mains power to ON (OnOff cluster)
set_node_attribute(matter_node, 1, 6, 0, True)
await trigger_subscription_callback(hass, matter_client)
state = hass.states.get("climate.room_airconditioner")
# test supported HVAC modes include fan and dry modes
assert state.attributes["hvac_modes"] == [
HVACMode.OFF,
HVACMode.HEAT,
HVACMode.COOL,
HVACMode.DRY,
HVACMode.FAN_ONLY,
HVACMode.HEAT_COOL,
]
# test fan-only hvac mode
set_node_attribute(matter_node, 1, 513, 28, 7)
await trigger_subscription_callback(hass, matter_client)
state = hass.states.get("climate.room_airconditioner")
assert state
assert state.state == HVACMode.FAN_ONLY
# test dry hvac mode
set_node_attribute(matter_node, 1, 513, 28, 8)
await trigger_subscription_callback(hass, matter_client)
state = hass.states.get("climate.room_airconditioner")
assert state
assert state.state == HVACMode.DRY
# test featuremap update
set_node_attribute(matter_node, 1, 513, 65532, 1)
await trigger_subscription_callback(hass, matter_client)
state = hass.states.get("climate.room_airconditioner")
assert state.attributes["supported_features"] & ClimateEntityFeature.TURN_ON